ee6b1cf90ee6a0631839f2749fba8fd5ee81dea7
[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         if (hw > 1) {
2702             Roo.get(he).setSize( ww * (1/hw),  ww);
2703             Roo.get(he).setX( (ww - (ww * (1/hw))/ 2);
2704         }
2705
2706     }
2707
2708     
2709 });
2710
2711 /*
2712  * - LGPL
2713  *
2714  * Card header - holder for the card header elements.
2715  * 
2716  */
2717
2718 /**
2719  * @class Roo.bootstrap.CardHeader
2720  * @extends Roo.bootstrap.Element
2721  * Bootstrap CardHeader class
2722  * @constructor
2723  * Create a new Card Header - that you can embed children into
2724  * @param {Object} config The config object
2725  */
2726
2727 Roo.bootstrap.CardHeader = function(config){
2728     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2729 };
2730
2731 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2732     
2733     
2734     container_method : 'getCardHeader' 
2735     
2736      
2737     
2738     
2739    
2740 });
2741
2742  
2743
2744  /*
2745  * - LGPL
2746  *
2747  * Card footer - holder for the card footer elements.
2748  * 
2749  */
2750
2751 /**
2752  * @class Roo.bootstrap.CardFooter
2753  * @extends Roo.bootstrap.Element
2754  * Bootstrap CardFooter class
2755  * @constructor
2756  * Create a new Card Footer - that you can embed children into
2757  * @param {Object} config The config object
2758  */
2759
2760 Roo.bootstrap.CardFooter = function(config){
2761     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2762 };
2763
2764 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2765     
2766     
2767     container_method : 'getCardFooter' 
2768     
2769      
2770     
2771     
2772    
2773 });
2774
2775  
2776
2777  /*
2778  * - LGPL
2779  *
2780  * Card header - holder for the card header elements.
2781  * 
2782  */
2783
2784 /**
2785  * @class Roo.bootstrap.CardImageTop
2786  * @extends Roo.bootstrap.Element
2787  * Bootstrap CardImageTop class
2788  * @constructor
2789  * Create a new Card Image Top container
2790  * @param {Object} config The config object
2791  */
2792
2793 Roo.bootstrap.CardImageTop = function(config){
2794     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2795 };
2796
2797 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2798     
2799    
2800     container_method : 'getCardImageTop' 
2801     
2802      
2803     
2804    
2805 });
2806
2807  
2808
2809  /*
2810  * - LGPL
2811  *
2812  * image
2813  * 
2814  */
2815
2816
2817 /**
2818  * @class Roo.bootstrap.Img
2819  * @extends Roo.bootstrap.Component
2820  * Bootstrap Img class
2821  * @cfg {Boolean} imgResponsive false | true
2822  * @cfg {String} border rounded | circle | thumbnail
2823  * @cfg {String} src image source
2824  * @cfg {String} alt image alternative text
2825  * @cfg {String} href a tag href
2826  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2827  * @cfg {String} xsUrl xs image source
2828  * @cfg {String} smUrl sm image source
2829  * @cfg {String} mdUrl md image source
2830  * @cfg {String} lgUrl lg image source
2831  * 
2832  * @constructor
2833  * Create a new Input
2834  * @param {Object} config The config object
2835  */
2836
2837 Roo.bootstrap.Img = function(config){
2838     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2839     
2840     this.addEvents({
2841         // img events
2842         /**
2843          * @event click
2844          * The img click event for the img.
2845          * @param {Roo.EventObject} e
2846          */
2847         "click" : true
2848     });
2849 };
2850
2851 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2852     
2853     imgResponsive: true,
2854     border: '',
2855     src: 'about:blank',
2856     href: false,
2857     target: false,
2858     xsUrl: '',
2859     smUrl: '',
2860     mdUrl: '',
2861     lgUrl: '',
2862
2863     getAutoCreate : function()
2864     {   
2865         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2866             return this.createSingleImg();
2867         }
2868         
2869         var cfg = {
2870             tag: 'div',
2871             cls: 'roo-image-responsive-group',
2872             cn: []
2873         };
2874         var _this = this;
2875         
2876         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2877             
2878             if(!_this[size + 'Url']){
2879                 return;
2880             }
2881             
2882             var img = {
2883                 tag: 'img',
2884                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2885                 html: _this.html || cfg.html,
2886                 src: _this[size + 'Url']
2887             };
2888             
2889             img.cls += ' roo-image-responsive-' + size;
2890             
2891             var s = ['xs', 'sm', 'md', 'lg'];
2892             
2893             s.splice(s.indexOf(size), 1);
2894             
2895             Roo.each(s, function(ss){
2896                 img.cls += ' hidden-' + ss;
2897             });
2898             
2899             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2900                 cfg.cls += ' img-' + _this.border;
2901             }
2902             
2903             if(_this.alt){
2904                 cfg.alt = _this.alt;
2905             }
2906             
2907             if(_this.href){
2908                 var a = {
2909                     tag: 'a',
2910                     href: _this.href,
2911                     cn: [
2912                         img
2913                     ]
2914                 };
2915
2916                 if(this.target){
2917                     a.target = _this.target;
2918                 }
2919             }
2920             
2921             cfg.cn.push((_this.href) ? a : img);
2922             
2923         });
2924         
2925         return cfg;
2926     },
2927     
2928     createSingleImg : function()
2929     {
2930         var cfg = {
2931             tag: 'img',
2932             cls: (this.imgResponsive) ? 'img-responsive' : '',
2933             html : null,
2934             src : 'about:blank'  // just incase src get's set to undefined?!?
2935         };
2936         
2937         cfg.html = this.html || cfg.html;
2938         
2939         cfg.src = this.src || cfg.src;
2940         
2941         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2942             cfg.cls += ' img-' + this.border;
2943         }
2944         
2945         if(this.alt){
2946             cfg.alt = this.alt;
2947         }
2948         
2949         if(this.href){
2950             var a = {
2951                 tag: 'a',
2952                 href: this.href,
2953                 cn: [
2954                     cfg
2955                 ]
2956             };
2957             
2958             if(this.target){
2959                 a.target = this.target;
2960             }
2961             
2962         }
2963         
2964         return (this.href) ? a : cfg;
2965     },
2966     
2967     initEvents: function() 
2968     {
2969         if(!this.href){
2970             this.el.on('click', this.onClick, this);
2971         }
2972         
2973     },
2974     
2975     onClick : function(e)
2976     {
2977         Roo.log('img onclick');
2978         this.fireEvent('click', this, e);
2979     },
2980     /**
2981      * Sets the url of the image - used to update it
2982      * @param {String} url the url of the image
2983      */
2984     
2985     setSrc : function(url)
2986     {
2987         this.src =  url;
2988         
2989         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2990             this.el.dom.src =  url;
2991             return;
2992         }
2993         
2994         this.el.select('img', true).first().dom.src =  url;
2995     }
2996     
2997     
2998    
2999 });
3000
3001  /*
3002  * - LGPL
3003  *
3004  * image
3005  * 
3006  */
3007
3008
3009 /**
3010  * @class Roo.bootstrap.Link
3011  * @extends Roo.bootstrap.Component
3012  * Bootstrap Link Class
3013  * @cfg {String} alt image alternative text
3014  * @cfg {String} href a tag href
3015  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3016  * @cfg {String} html the content of the link.
3017  * @cfg {String} anchor name for the anchor link
3018  * @cfg {String} fa - favicon
3019
3020  * @cfg {Boolean} preventDefault (true | false) default false
3021
3022  * 
3023  * @constructor
3024  * Create a new Input
3025  * @param {Object} config The config object
3026  */
3027
3028 Roo.bootstrap.Link = function(config){
3029     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3030     
3031     this.addEvents({
3032         // img events
3033         /**
3034          * @event click
3035          * The img click event for the img.
3036          * @param {Roo.EventObject} e
3037          */
3038         "click" : true
3039     });
3040 };
3041
3042 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3043     
3044     href: false,
3045     target: false,
3046     preventDefault: false,
3047     anchor : false,
3048     alt : false,
3049     fa: false,
3050
3051
3052     getAutoCreate : function()
3053     {
3054         var html = this.html || '';
3055         
3056         if (this.fa !== false) {
3057             html = '<i class="fa fa-' + this.fa + '"></i>';
3058         }
3059         var cfg = {
3060             tag: 'a'
3061         };
3062         // anchor's do not require html/href...
3063         if (this.anchor === false) {
3064             cfg.html = html;
3065             cfg.href = this.href || '#';
3066         } else {
3067             cfg.name = this.anchor;
3068             if (this.html !== false || this.fa !== false) {
3069                 cfg.html = html;
3070             }
3071             if (this.href !== false) {
3072                 cfg.href = this.href;
3073             }
3074         }
3075         
3076         if(this.alt !== false){
3077             cfg.alt = this.alt;
3078         }
3079         
3080         
3081         if(this.target !== false) {
3082             cfg.target = this.target;
3083         }
3084         
3085         return cfg;
3086     },
3087     
3088     initEvents: function() {
3089         
3090         if(!this.href || this.preventDefault){
3091             this.el.on('click', this.onClick, this);
3092         }
3093     },
3094     
3095     onClick : function(e)
3096     {
3097         if(this.preventDefault){
3098             e.preventDefault();
3099         }
3100         //Roo.log('img onclick');
3101         this.fireEvent('click', this, e);
3102     }
3103    
3104 });
3105
3106  /*
3107  * - LGPL
3108  *
3109  * header
3110  * 
3111  */
3112
3113 /**
3114  * @class Roo.bootstrap.Header
3115  * @extends Roo.bootstrap.Component
3116  * Bootstrap Header class
3117  * @cfg {String} html content of header
3118  * @cfg {Number} level (1|2|3|4|5|6) default 1
3119  * 
3120  * @constructor
3121  * Create a new Header
3122  * @param {Object} config The config object
3123  */
3124
3125
3126 Roo.bootstrap.Header  = function(config){
3127     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3128 };
3129
3130 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3131     
3132     //href : false,
3133     html : false,
3134     level : 1,
3135     
3136     
3137     
3138     getAutoCreate : function(){
3139         
3140         
3141         
3142         var cfg = {
3143             tag: 'h' + (1 *this.level),
3144             html: this.html || ''
3145         } ;
3146         
3147         return cfg;
3148     }
3149    
3150 });
3151
3152  
3153
3154  /*
3155  * Based on:
3156  * Ext JS Library 1.1.1
3157  * Copyright(c) 2006-2007, Ext JS, LLC.
3158  *
3159  * Originally Released Under LGPL - original licence link has changed is not relivant.
3160  *
3161  * Fork - LGPL
3162  * <script type="text/javascript">
3163  */
3164  
3165 /**
3166  * @class Roo.bootstrap.MenuMgr
3167  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3168  * @singleton
3169  */
3170 Roo.bootstrap.MenuMgr = function(){
3171    var menus, active, groups = {}, attached = false, lastShow = new Date();
3172
3173    // private - called when first menu is created
3174    function init(){
3175        menus = {};
3176        active = new Roo.util.MixedCollection();
3177        Roo.get(document).addKeyListener(27, function(){
3178            if(active.length > 0){
3179                hideAll();
3180            }
3181        });
3182    }
3183
3184    // private
3185    function hideAll(){
3186        if(active && active.length > 0){
3187            var c = active.clone();
3188            c.each(function(m){
3189                m.hide();
3190            });
3191        }
3192    }
3193
3194    // private
3195    function onHide(m){
3196        active.remove(m);
3197        if(active.length < 1){
3198            Roo.get(document).un("mouseup", onMouseDown);
3199             
3200            attached = false;
3201        }
3202    }
3203
3204    // private
3205    function onShow(m){
3206        var last = active.last();
3207        lastShow = new Date();
3208        active.add(m);
3209        if(!attached){
3210           Roo.get(document).on("mouseup", onMouseDown);
3211            
3212            attached = true;
3213        }
3214        if(m.parentMenu){
3215           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3216           m.parentMenu.activeChild = m;
3217        }else if(last && last.isVisible()){
3218           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3219        }
3220    }
3221
3222    // private
3223    function onBeforeHide(m){
3224        if(m.activeChild){
3225            m.activeChild.hide();
3226        }
3227        if(m.autoHideTimer){
3228            clearTimeout(m.autoHideTimer);
3229            delete m.autoHideTimer;
3230        }
3231    }
3232
3233    // private
3234    function onBeforeShow(m){
3235        var pm = m.parentMenu;
3236        if(!pm && !m.allowOtherMenus){
3237            hideAll();
3238        }else if(pm && pm.activeChild && active != m){
3239            pm.activeChild.hide();
3240        }
3241    }
3242
3243    // private this should really trigger on mouseup..
3244    function onMouseDown(e){
3245         Roo.log("on Mouse Up");
3246         
3247         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3248             Roo.log("MenuManager hideAll");
3249             hideAll();
3250             e.stopEvent();
3251         }
3252         
3253         
3254    }
3255
3256    // private
3257    function onBeforeCheck(mi, state){
3258        if(state){
3259            var g = groups[mi.group];
3260            for(var i = 0, l = g.length; i < l; i++){
3261                if(g[i] != mi){
3262                    g[i].setChecked(false);
3263                }
3264            }
3265        }
3266    }
3267
3268    return {
3269
3270        /**
3271         * Hides all menus that are currently visible
3272         */
3273        hideAll : function(){
3274             hideAll();  
3275        },
3276
3277        // private
3278        register : function(menu){
3279            if(!menus){
3280                init();
3281            }
3282            menus[menu.id] = menu;
3283            menu.on("beforehide", onBeforeHide);
3284            menu.on("hide", onHide);
3285            menu.on("beforeshow", onBeforeShow);
3286            menu.on("show", onShow);
3287            var g = menu.group;
3288            if(g && menu.events["checkchange"]){
3289                if(!groups[g]){
3290                    groups[g] = [];
3291                }
3292                groups[g].push(menu);
3293                menu.on("checkchange", onCheck);
3294            }
3295        },
3296
3297         /**
3298          * Returns a {@link Roo.menu.Menu} object
3299          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3300          * be used to generate and return a new Menu instance.
3301          */
3302        get : function(menu){
3303            if(typeof menu == "string"){ // menu id
3304                return menus[menu];
3305            }else if(menu.events){  // menu instance
3306                return menu;
3307            }
3308            /*else if(typeof menu.length == 'number'){ // array of menu items?
3309                return new Roo.bootstrap.Menu({items:menu});
3310            }else{ // otherwise, must be a config
3311                return new Roo.bootstrap.Menu(menu);
3312            }
3313            */
3314            return false;
3315        },
3316
3317        // private
3318        unregister : function(menu){
3319            delete menus[menu.id];
3320            menu.un("beforehide", onBeforeHide);
3321            menu.un("hide", onHide);
3322            menu.un("beforeshow", onBeforeShow);
3323            menu.un("show", onShow);
3324            var g = menu.group;
3325            if(g && menu.events["checkchange"]){
3326                groups[g].remove(menu);
3327                menu.un("checkchange", onCheck);
3328            }
3329        },
3330
3331        // private
3332        registerCheckable : function(menuItem){
3333            var g = menuItem.group;
3334            if(g){
3335                if(!groups[g]){
3336                    groups[g] = [];
3337                }
3338                groups[g].push(menuItem);
3339                menuItem.on("beforecheckchange", onBeforeCheck);
3340            }
3341        },
3342
3343        // private
3344        unregisterCheckable : function(menuItem){
3345            var g = menuItem.group;
3346            if(g){
3347                groups[g].remove(menuItem);
3348                menuItem.un("beforecheckchange", onBeforeCheck);
3349            }
3350        }
3351    };
3352 }();/*
3353  * - LGPL
3354  *
3355  * menu
3356  * 
3357  */
3358
3359 /**
3360  * @class Roo.bootstrap.Menu
3361  * @extends Roo.bootstrap.Component
3362  * Bootstrap Menu class - container for MenuItems
3363  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3364  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3365  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3366  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3367  * 
3368  * @constructor
3369  * Create a new Menu
3370  * @param {Object} config The config object
3371  */
3372
3373
3374 Roo.bootstrap.Menu = function(config){
3375     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3376     if (this.registerMenu && this.type != 'treeview')  {
3377         Roo.bootstrap.MenuMgr.register(this);
3378     }
3379     
3380     
3381     this.addEvents({
3382         /**
3383          * @event beforeshow
3384          * Fires before this menu is displayed (return false to block)
3385          * @param {Roo.menu.Menu} this
3386          */
3387         beforeshow : true,
3388         /**
3389          * @event beforehide
3390          * Fires before this menu is hidden (return false to block)
3391          * @param {Roo.menu.Menu} this
3392          */
3393         beforehide : true,
3394         /**
3395          * @event show
3396          * Fires after this menu is displayed
3397          * @param {Roo.menu.Menu} this
3398          */
3399         show : true,
3400         /**
3401          * @event hide
3402          * Fires after this menu is hidden
3403          * @param {Roo.menu.Menu} this
3404          */
3405         hide : true,
3406         /**
3407          * @event click
3408          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3409          * @param {Roo.menu.Menu} this
3410          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         click : true,
3414         /**
3415          * @event mouseover
3416          * Fires when the mouse is hovering over this menu
3417          * @param {Roo.menu.Menu} this
3418          * @param {Roo.EventObject} e
3419          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3420          */
3421         mouseover : true,
3422         /**
3423          * @event mouseout
3424          * Fires when the mouse exits this menu
3425          * @param {Roo.menu.Menu} this
3426          * @param {Roo.EventObject} e
3427          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3428          */
3429         mouseout : true,
3430         /**
3431          * @event itemclick
3432          * Fires when a menu item contained in this menu is clicked
3433          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3434          * @param {Roo.EventObject} e
3435          */
3436         itemclick: true
3437     });
3438     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3439 };
3440
3441 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3442     
3443    /// html : false,
3444     //align : '',
3445     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3446     type: false,
3447     /**
3448      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3449      */
3450     registerMenu : true,
3451     
3452     menuItems :false, // stores the menu items..
3453     
3454     hidden:true,
3455         
3456     parentMenu : false,
3457     
3458     stopEvent : true,
3459     
3460     isLink : false,
3461     
3462     getChildContainer : function() {
3463         return this.el;  
3464     },
3465     
3466     getAutoCreate : function(){
3467          
3468         //if (['right'].indexOf(this.align)!==-1) {
3469         //    cfg.cn[1].cls += ' pull-right'
3470         //}
3471         
3472         
3473         var cfg = {
3474             tag : 'ul',
3475             cls : 'dropdown-menu' ,
3476             style : 'z-index:1000'
3477             
3478         };
3479         
3480         if (this.type === 'submenu') {
3481             cfg.cls = 'submenu active';
3482         }
3483         if (this.type === 'treeview') {
3484             cfg.cls = 'treeview-menu';
3485         }
3486         
3487         return cfg;
3488     },
3489     initEvents : function() {
3490         
3491        // Roo.log("ADD event");
3492        // Roo.log(this.triggerEl.dom);
3493         
3494         this.triggerEl.on('click', this.onTriggerClick, this);
3495         
3496         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3497         
3498         
3499         if (this.triggerEl.hasClass('nav-item')) {
3500             // dropdown toggle on the 'a' in BS4?
3501             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3502         } else {
3503             this.triggerEl.addClass('dropdown-toggle');
3504         }
3505         if (Roo.isTouch) {
3506             this.el.on('touchstart'  , this.onTouch, this);
3507         }
3508         this.el.on('click' , this.onClick, this);
3509
3510         this.el.on("mouseover", this.onMouseOver, this);
3511         this.el.on("mouseout", this.onMouseOut, this);
3512         
3513     },
3514     
3515     findTargetItem : function(e)
3516     {
3517         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3518         if(!t){
3519             return false;
3520         }
3521         //Roo.log(t);         Roo.log(t.id);
3522         if(t && t.id){
3523             //Roo.log(this.menuitems);
3524             return this.menuitems.get(t.id);
3525             
3526             //return this.items.get(t.menuItemId);
3527         }
3528         
3529         return false;
3530     },
3531     
3532     onTouch : function(e) 
3533     {
3534         Roo.log("menu.onTouch");
3535         //e.stopEvent(); this make the user popdown broken
3536         this.onClick(e);
3537     },
3538     
3539     onClick : function(e)
3540     {
3541         Roo.log("menu.onClick");
3542         
3543         var t = this.findTargetItem(e);
3544         if(!t || t.isContainer){
3545             return;
3546         }
3547         Roo.log(e);
3548         /*
3549         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3550             if(t == this.activeItem && t.shouldDeactivate(e)){
3551                 this.activeItem.deactivate();
3552                 delete this.activeItem;
3553                 return;
3554             }
3555             if(t.canActivate){
3556                 this.setActiveItem(t, true);
3557             }
3558             return;
3559             
3560             
3561         }
3562         */
3563        
3564         Roo.log('pass click event');
3565         
3566         t.onClick(e);
3567         
3568         this.fireEvent("click", this, t, e);
3569         
3570         var _this = this;
3571         
3572         if(!t.href.length || t.href == '#'){
3573             (function() { _this.hide(); }).defer(100);
3574         }
3575         
3576     },
3577     
3578     onMouseOver : function(e){
3579         var t  = this.findTargetItem(e);
3580         //Roo.log(t);
3581         //if(t){
3582         //    if(t.canActivate && !t.disabled){
3583         //        this.setActiveItem(t, true);
3584         //    }
3585         //}
3586         
3587         this.fireEvent("mouseover", this, e, t);
3588     },
3589     isVisible : function(){
3590         return !this.hidden;
3591     },
3592     onMouseOut : function(e){
3593         var t  = this.findTargetItem(e);
3594         
3595         //if(t ){
3596         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3597         //        this.activeItem.deactivate();
3598         //        delete this.activeItem;
3599         //    }
3600         //}
3601         this.fireEvent("mouseout", this, e, t);
3602     },
3603     
3604     
3605     /**
3606      * Displays this menu relative to another element
3607      * @param {String/HTMLElement/Roo.Element} element The element to align to
3608      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3609      * the element (defaults to this.defaultAlign)
3610      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3611      */
3612     show : function(el, pos, parentMenu)
3613     {
3614         if (false === this.fireEvent("beforeshow", this)) {
3615             Roo.log("show canceled");
3616             return;
3617         }
3618         this.parentMenu = parentMenu;
3619         if(!this.el){
3620             this.render();
3621         }
3622         
3623         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3624     },
3625      /**
3626      * Displays this menu at a specific xy position
3627      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3628      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3629      */
3630     showAt : function(xy, parentMenu, /* private: */_e){
3631         this.parentMenu = parentMenu;
3632         if(!this.el){
3633             this.render();
3634         }
3635         if(_e !== false){
3636             this.fireEvent("beforeshow", this);
3637             //xy = this.el.adjustForConstraints(xy);
3638         }
3639         
3640         //this.el.show();
3641         this.hideMenuItems();
3642         this.hidden = false;
3643         this.triggerEl.addClass('open');
3644         this.el.addClass('show');
3645         
3646         // reassign x when hitting right
3647         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3648             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3649         }
3650         
3651         // reassign y when hitting bottom
3652         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3653             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3654         }
3655         
3656         // but the list may align on trigger left or trigger top... should it be a properity?
3657         
3658         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3659             this.el.setXY(xy);
3660         }
3661         
3662         this.focus();
3663         this.fireEvent("show", this);
3664     },
3665     
3666     focus : function(){
3667         return;
3668         if(!this.hidden){
3669             this.doFocus.defer(50, this);
3670         }
3671     },
3672
3673     doFocus : function(){
3674         if(!this.hidden){
3675             this.focusEl.focus();
3676         }
3677     },
3678
3679     /**
3680      * Hides this menu and optionally all parent menus
3681      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3682      */
3683     hide : function(deep)
3684     {
3685         if (false === this.fireEvent("beforehide", this)) {
3686             Roo.log("hide canceled");
3687             return;
3688         }
3689         this.hideMenuItems();
3690         if(this.el && this.isVisible()){
3691            
3692             if(this.activeItem){
3693                 this.activeItem.deactivate();
3694                 this.activeItem = null;
3695             }
3696             this.triggerEl.removeClass('open');;
3697             this.el.removeClass('show');
3698             this.hidden = true;
3699             this.fireEvent("hide", this);
3700         }
3701         if(deep === true && this.parentMenu){
3702             this.parentMenu.hide(true);
3703         }
3704     },
3705     
3706     onTriggerClick : function(e)
3707     {
3708         Roo.log('trigger click');
3709         
3710         var target = e.getTarget();
3711         
3712         Roo.log(target.nodeName.toLowerCase());
3713         
3714         if(target.nodeName.toLowerCase() === 'i'){
3715             e.preventDefault();
3716         }
3717         
3718     },
3719     
3720     onTriggerPress  : function(e)
3721     {
3722         Roo.log('trigger press');
3723         //Roo.log(e.getTarget());
3724        // Roo.log(this.triggerEl.dom);
3725        
3726         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3727         var pel = Roo.get(e.getTarget());
3728         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3729             Roo.log('is treeview or dropdown?');
3730             return;
3731         }
3732         
3733         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3734             return;
3735         }
3736         
3737         if (this.isVisible()) {
3738             Roo.log('hide');
3739             this.hide();
3740         } else {
3741             Roo.log('show');
3742             this.show(this.triggerEl, '?', false);
3743         }
3744         
3745         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3746             e.stopEvent();
3747         }
3748         
3749     },
3750        
3751     
3752     hideMenuItems : function()
3753     {
3754         Roo.log("hide Menu Items");
3755         if (!this.el) { 
3756             return;
3757         }
3758         
3759         this.el.select('.open',true).each(function(aa) {
3760             
3761             aa.removeClass('open');
3762          
3763         });
3764     },
3765     addxtypeChild : function (tree, cntr) {
3766         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3767           
3768         this.menuitems.add(comp);
3769         return comp;
3770
3771     },
3772     getEl : function()
3773     {
3774         Roo.log(this.el);
3775         return this.el;
3776     },
3777     
3778     clear : function()
3779     {
3780         this.getEl().dom.innerHTML = '';
3781         this.menuitems.clear();
3782     }
3783 });
3784
3785  
3786  /*
3787  * - LGPL
3788  *
3789  * menu item
3790  * 
3791  */
3792
3793
3794 /**
3795  * @class Roo.bootstrap.MenuItem
3796  * @extends Roo.bootstrap.Component
3797  * Bootstrap MenuItem class
3798  * @cfg {String} html the menu label
3799  * @cfg {String} href the link
3800  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3801  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3802  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3803  * @cfg {String} fa favicon to show on left of menu item.
3804  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3805  * 
3806  * 
3807  * @constructor
3808  * Create a new MenuItem
3809  * @param {Object} config The config object
3810  */
3811
3812
3813 Roo.bootstrap.MenuItem = function(config){
3814     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3815     this.addEvents({
3816         // raw events
3817         /**
3818          * @event click
3819          * The raw click event for the entire grid.
3820          * @param {Roo.bootstrap.MenuItem} this
3821          * @param {Roo.EventObject} e
3822          */
3823         "click" : true
3824     });
3825 };
3826
3827 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3828     
3829     href : false,
3830     html : false,
3831     preventDefault: false,
3832     isContainer : false,
3833     active : false,
3834     fa: false,
3835     
3836     getAutoCreate : function(){
3837         
3838         if(this.isContainer){
3839             return {
3840                 tag: 'li',
3841                 cls: 'dropdown-menu-item '
3842             };
3843         }
3844         var ctag = {
3845             tag: 'span',
3846             html: 'Link'
3847         };
3848         
3849         var anc = {
3850             tag : 'a',
3851             cls : 'dropdown-item',
3852             href : '#',
3853             cn : [  ]
3854         };
3855         
3856         if (this.fa !== false) {
3857             anc.cn.push({
3858                 tag : 'i',
3859                 cls : 'fa fa-' + this.fa
3860             });
3861         }
3862         
3863         anc.cn.push(ctag);
3864         
3865         
3866         var cfg= {
3867             tag: 'li',
3868             cls: 'dropdown-menu-item',
3869             cn: [ anc ]
3870         };
3871         if (this.parent().type == 'treeview') {
3872             cfg.cls = 'treeview-menu';
3873         }
3874         if (this.active) {
3875             cfg.cls += ' active';
3876         }
3877         
3878         
3879         
3880         anc.href = this.href || cfg.cn[0].href ;
3881         ctag.html = this.html || cfg.cn[0].html ;
3882         return cfg;
3883     },
3884     
3885     initEvents: function()
3886     {
3887         if (this.parent().type == 'treeview') {
3888             this.el.select('a').on('click', this.onClick, this);
3889         }
3890         
3891         if (this.menu) {
3892             this.menu.parentType = this.xtype;
3893             this.menu.triggerEl = this.el;
3894             this.menu = this.addxtype(Roo.apply({}, this.menu));
3895         }
3896         
3897     },
3898     onClick : function(e)
3899     {
3900         Roo.log('item on click ');
3901         
3902         if(this.preventDefault){
3903             e.preventDefault();
3904         }
3905         //this.parent().hideMenuItems();
3906         
3907         this.fireEvent('click', this, e);
3908     },
3909     getEl : function()
3910     {
3911         return this.el;
3912     } 
3913 });
3914
3915  
3916
3917  /*
3918  * - LGPL
3919  *
3920  * menu separator
3921  * 
3922  */
3923
3924
3925 /**
3926  * @class Roo.bootstrap.MenuSeparator
3927  * @extends Roo.bootstrap.Component
3928  * Bootstrap MenuSeparator class
3929  * 
3930  * @constructor
3931  * Create a new MenuItem
3932  * @param {Object} config The config object
3933  */
3934
3935
3936 Roo.bootstrap.MenuSeparator = function(config){
3937     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3938 };
3939
3940 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3941     
3942     getAutoCreate : function(){
3943         var cfg = {
3944             cls: 'divider',
3945             tag : 'li'
3946         };
3947         
3948         return cfg;
3949     }
3950    
3951 });
3952
3953  
3954
3955  
3956 /*
3957 * Licence: LGPL
3958 */
3959
3960 /**
3961  * @class Roo.bootstrap.Modal
3962  * @extends Roo.bootstrap.Component
3963  * Bootstrap Modal class
3964  * @cfg {String} title Title of dialog
3965  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3966  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3967  * @cfg {Boolean} specificTitle default false
3968  * @cfg {Array} buttons Array of buttons or standard button set..
3969  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3970  * @cfg {Boolean} animate default true
3971  * @cfg {Boolean} allow_close default true
3972  * @cfg {Boolean} fitwindow default false
3973  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3974  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3975  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3976  * @cfg {String} size (sm|lg|xl) default empty
3977  * @cfg {Number} max_width set the max width of modal
3978  * @cfg {Boolean} editableTitle can the title be edited
3979
3980  *
3981  *
3982  * @constructor
3983  * Create a new Modal Dialog
3984  * @param {Object} config The config object
3985  */
3986
3987 Roo.bootstrap.Modal = function(config){
3988     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3989     this.addEvents({
3990         // raw events
3991         /**
3992          * @event btnclick
3993          * The raw btnclick event for the button
3994          * @param {Roo.EventObject} e
3995          */
3996         "btnclick" : true,
3997         /**
3998          * @event resize
3999          * Fire when dialog resize
4000          * @param {Roo.bootstrap.Modal} this
4001          * @param {Roo.EventObject} e
4002          */
4003         "resize" : true,
4004         /**
4005          * @event titlechanged
4006          * Fire when the editable title has been changed
4007          * @param {Roo.bootstrap.Modal} this
4008          * @param {Roo.EventObject} value
4009          */
4010         "titlechanged" : true 
4011         
4012     });
4013     this.buttons = this.buttons || [];
4014
4015     if (this.tmpl) {
4016         this.tmpl = Roo.factory(this.tmpl);
4017     }
4018
4019 };
4020
4021 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4022
4023     title : 'test dialog',
4024
4025     buttons : false,
4026
4027     // set on load...
4028
4029     html: false,
4030
4031     tmp: false,
4032
4033     specificTitle: false,
4034
4035     buttonPosition: 'right',
4036
4037     allow_close : true,
4038
4039     animate : true,
4040
4041     fitwindow: false,
4042     
4043      // private
4044     dialogEl: false,
4045     bodyEl:  false,
4046     footerEl:  false,
4047     titleEl:  false,
4048     closeEl:  false,
4049
4050     size: '',
4051     
4052     max_width: 0,
4053     
4054     max_height: 0,
4055     
4056     fit_content: false,
4057     editableTitle  : false,
4058
4059     onRender : function(ct, position)
4060     {
4061         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4062
4063         if(!this.el){
4064             var cfg = Roo.apply({},  this.getAutoCreate());
4065             cfg.id = Roo.id();
4066             //if(!cfg.name){
4067             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4068             //}
4069             //if (!cfg.name.length) {
4070             //    delete cfg.name;
4071            // }
4072             if (this.cls) {
4073                 cfg.cls += ' ' + this.cls;
4074             }
4075             if (this.style) {
4076                 cfg.style = this.style;
4077             }
4078             this.el = Roo.get(document.body).createChild(cfg, position);
4079         }
4080         //var type = this.el.dom.type;
4081
4082
4083         if(this.tabIndex !== undefined){
4084             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4085         }
4086
4087         this.dialogEl = this.el.select('.modal-dialog',true).first();
4088         this.bodyEl = this.el.select('.modal-body',true).first();
4089         this.closeEl = this.el.select('.modal-header .close', true).first();
4090         this.headerEl = this.el.select('.modal-header',true).first();
4091         this.titleEl = this.el.select('.modal-title',true).first();
4092         this.footerEl = this.el.select('.modal-footer',true).first();
4093
4094         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4095         
4096         //this.el.addClass("x-dlg-modal");
4097
4098         if (this.buttons.length) {
4099             Roo.each(this.buttons, function(bb) {
4100                 var b = Roo.apply({}, bb);
4101                 b.xns = b.xns || Roo.bootstrap;
4102                 b.xtype = b.xtype || 'Button';
4103                 if (typeof(b.listeners) == 'undefined') {
4104                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4105                 }
4106
4107                 var btn = Roo.factory(b);
4108
4109                 btn.render(this.getButtonContainer());
4110
4111             },this);
4112         }
4113         // render the children.
4114         var nitems = [];
4115
4116         if(typeof(this.items) != 'undefined'){
4117             var items = this.items;
4118             delete this.items;
4119
4120             for(var i =0;i < items.length;i++) {
4121                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4122             }
4123         }
4124
4125         this.items = nitems;
4126
4127         // where are these used - they used to be body/close/footer
4128
4129
4130         this.initEvents();
4131         //this.el.addClass([this.fieldClass, this.cls]);
4132
4133     },
4134
4135     getAutoCreate : function()
4136     {
4137         // we will default to modal-body-overflow - might need to remove or make optional later.
4138         var bdy = {
4139                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4140                 html : this.html || ''
4141         };
4142
4143         var title = {
4144             tag: 'h5',
4145             cls : 'modal-title',
4146             html : this.title
4147         };
4148
4149         if(this.specificTitle){ // WTF is this?
4150             title = this.title;
4151         }
4152
4153         var header = [];
4154         if (this.allow_close && Roo.bootstrap.version == 3) {
4155             header.push({
4156                 tag: 'button',
4157                 cls : 'close',
4158                 html : '&times'
4159             });
4160         }
4161
4162         header.push(title);
4163
4164         if (this.editableTitle) {
4165             header.push({
4166                 cls: 'form-control roo-editable-title d-none',
4167                 tag: 'input',
4168                 type: 'text'
4169             });
4170         }
4171         
4172         if (this.allow_close && Roo.bootstrap.version == 4) {
4173             header.push({
4174                 tag: 'button',
4175                 cls : 'close',
4176                 html : '&times'
4177             });
4178         }
4179         
4180         var size = '';
4181
4182         if(this.size.length){
4183             size = 'modal-' + this.size;
4184         }
4185         
4186         var footer = Roo.bootstrap.version == 3 ?
4187             {
4188                 cls : 'modal-footer',
4189                 cn : [
4190                     {
4191                         tag: 'div',
4192                         cls: 'btn-' + this.buttonPosition
4193                     }
4194                 ]
4195
4196             } :
4197             {  // BS4 uses mr-auto on left buttons....
4198                 cls : 'modal-footer'
4199             };
4200
4201             
4202
4203         
4204         
4205         var modal = {
4206             cls: "modal",
4207              cn : [
4208                 {
4209                     cls: "modal-dialog " + size,
4210                     cn : [
4211                         {
4212                             cls : "modal-content",
4213                             cn : [
4214                                 {
4215                                     cls : 'modal-header',
4216                                     cn : header
4217                                 },
4218                                 bdy,
4219                                 footer
4220                             ]
4221
4222                         }
4223                     ]
4224
4225                 }
4226             ]
4227         };
4228
4229         if(this.animate){
4230             modal.cls += ' fade';
4231         }
4232
4233         return modal;
4234
4235     },
4236     getChildContainer : function() {
4237
4238          return this.bodyEl;
4239
4240     },
4241     getButtonContainer : function() {
4242         
4243          return Roo.bootstrap.version == 4 ?
4244             this.el.select('.modal-footer',true).first()
4245             : this.el.select('.modal-footer div',true).first();
4246
4247     },
4248     initEvents : function()
4249     {
4250         if (this.allow_close) {
4251             this.closeEl.on('click', this.hide, this);
4252         }
4253         Roo.EventManager.onWindowResize(this.resize, this, true);
4254         if (this.editableTitle) {
4255             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4256             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4257             this.headerEditEl.on('keyup', function(e) {
4258                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4259                         this.toggleHeaderInput(false)
4260                     }
4261                 }, this);
4262             this.headerEditEl.on('blur', function(e) {
4263                 this.toggleHeaderInput(false)
4264             },this);
4265         }
4266
4267     },
4268   
4269
4270     resize : function()
4271     {
4272         this.maskEl.setSize(
4273             Roo.lib.Dom.getViewWidth(true),
4274             Roo.lib.Dom.getViewHeight(true)
4275         );
4276         
4277         if (this.fitwindow) {
4278             
4279            this.dialogEl.setStyle( { 'max-width' : '100%' });
4280             this.setSize(
4281                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4282                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4283             );
4284             return;
4285         }
4286         
4287         if(this.max_width !== 0) {
4288             
4289             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4290             
4291             if(this.height) {
4292                 this.setSize(w, this.height);
4293                 return;
4294             }
4295             
4296             if(this.max_height) {
4297                 this.setSize(w,Math.min(
4298                     this.max_height,
4299                     Roo.lib.Dom.getViewportHeight(true) - 60
4300                 ));
4301                 
4302                 return;
4303             }
4304             
4305             if(!this.fit_content) {
4306                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4307                 return;
4308             }
4309             
4310             this.setSize(w, Math.min(
4311                 60 +
4312                 this.headerEl.getHeight() + 
4313                 this.footerEl.getHeight() + 
4314                 this.getChildHeight(this.bodyEl.dom.childNodes),
4315                 Roo.lib.Dom.getViewportHeight(true) - 60)
4316             );
4317         }
4318         
4319     },
4320
4321     setSize : function(w,h)
4322     {
4323         if (!w && !h) {
4324             return;
4325         }
4326         
4327         this.resizeTo(w,h);
4328     },
4329
4330     show : function() {
4331
4332         if (!this.rendered) {
4333             this.render();
4334         }
4335         this.toggleHeaderInput(false);
4336         //this.el.setStyle('display', 'block');
4337         this.el.removeClass('hideing');
4338         this.el.dom.style.display='block';
4339         
4340         Roo.get(document.body).addClass('modal-open');
4341  
4342         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4343             
4344             (function(){
4345                 this.el.addClass('show');
4346                 this.el.addClass('in');
4347             }).defer(50, this);
4348         }else{
4349             this.el.addClass('show');
4350             this.el.addClass('in');
4351         }
4352
4353         // not sure how we can show data in here..
4354         //if (this.tmpl) {
4355         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4356         //}
4357
4358         Roo.get(document.body).addClass("x-body-masked");
4359         
4360         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4361         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4362         this.maskEl.dom.style.display = 'block';
4363         this.maskEl.addClass('show');
4364         
4365         
4366         this.resize();
4367         
4368         this.fireEvent('show', this);
4369
4370         // set zindex here - otherwise it appears to be ignored...
4371         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4372
4373         (function () {
4374             this.items.forEach( function(e) {
4375                 e.layout ? e.layout() : false;
4376
4377             });
4378         }).defer(100,this);
4379
4380     },
4381     hide : function()
4382     {
4383         if(this.fireEvent("beforehide", this) !== false){
4384             
4385             this.maskEl.removeClass('show');
4386             
4387             this.maskEl.dom.style.display = '';
4388             Roo.get(document.body).removeClass("x-body-masked");
4389             this.el.removeClass('in');
4390             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4391
4392             if(this.animate){ // why
4393                 this.el.addClass('hideing');
4394                 this.el.removeClass('show');
4395                 (function(){
4396                     if (!this.el.hasClass('hideing')) {
4397                         return; // it's been shown again...
4398                     }
4399                     
4400                     this.el.dom.style.display='';
4401
4402                     Roo.get(document.body).removeClass('modal-open');
4403                     this.el.removeClass('hideing');
4404                 }).defer(150,this);
4405                 
4406             }else{
4407                 this.el.removeClass('show');
4408                 this.el.dom.style.display='';
4409                 Roo.get(document.body).removeClass('modal-open');
4410
4411             }
4412             this.fireEvent('hide', this);
4413         }
4414     },
4415     isVisible : function()
4416     {
4417         
4418         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4419         
4420     },
4421
4422     addButton : function(str, cb)
4423     {
4424
4425
4426         var b = Roo.apply({}, { html : str } );
4427         b.xns = b.xns || Roo.bootstrap;
4428         b.xtype = b.xtype || 'Button';
4429         if (typeof(b.listeners) == 'undefined') {
4430             b.listeners = { click : cb.createDelegate(this)  };
4431         }
4432
4433         var btn = Roo.factory(b);
4434
4435         btn.render(this.getButtonContainer());
4436
4437         return btn;
4438
4439     },
4440
4441     setDefaultButton : function(btn)
4442     {
4443         //this.el.select('.modal-footer').()
4444     },
4445
4446     resizeTo: function(w,h)
4447     {
4448         this.dialogEl.setWidth(w);
4449         
4450         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4451
4452         this.bodyEl.setHeight(h - diff);
4453         
4454         this.fireEvent('resize', this);
4455     },
4456     
4457     setContentSize  : function(w, h)
4458     {
4459
4460     },
4461     onButtonClick: function(btn,e)
4462     {
4463         //Roo.log([a,b,c]);
4464         this.fireEvent('btnclick', btn.name, e);
4465     },
4466      /**
4467      * Set the title of the Dialog
4468      * @param {String} str new Title
4469      */
4470     setTitle: function(str) {
4471         this.titleEl.dom.innerHTML = str;
4472         this.title = str;
4473     },
4474     /**
4475      * Set the body of the Dialog
4476      * @param {String} str new Title
4477      */
4478     setBody: function(str) {
4479         this.bodyEl.dom.innerHTML = str;
4480     },
4481     /**
4482      * Set the body of the Dialog using the template
4483      * @param {Obj} data - apply this data to the template and replace the body contents.
4484      */
4485     applyBody: function(obj)
4486     {
4487         if (!this.tmpl) {
4488             Roo.log("Error - using apply Body without a template");
4489             //code
4490         }
4491         this.tmpl.overwrite(this.bodyEl, obj);
4492     },
4493     
4494     getChildHeight : function(child_nodes)
4495     {
4496         if(
4497             !child_nodes ||
4498             child_nodes.length == 0
4499         ) {
4500             return 0;
4501         }
4502         
4503         var child_height = 0;
4504         
4505         for(var i = 0; i < child_nodes.length; i++) {
4506             
4507             /*
4508             * for modal with tabs...
4509             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4510                 
4511                 var layout_childs = child_nodes[i].childNodes;
4512                 
4513                 for(var j = 0; j < layout_childs.length; j++) {
4514                     
4515                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4516                         
4517                         var layout_body_childs = layout_childs[j].childNodes;
4518                         
4519                         for(var k = 0; k < layout_body_childs.length; k++) {
4520                             
4521                             if(layout_body_childs[k].classList.contains('navbar')) {
4522                                 child_height += layout_body_childs[k].offsetHeight;
4523                                 continue;
4524                             }
4525                             
4526                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4527                                 
4528                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4529                                 
4530                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4531                                     
4532                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4533                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4534                                         continue;
4535                                     }
4536                                     
4537                                 }
4538                                 
4539                             }
4540                             
4541                         }
4542                     }
4543                 }
4544                 continue;
4545             }
4546             */
4547             
4548             child_height += child_nodes[i].offsetHeight;
4549             // Roo.log(child_nodes[i].offsetHeight);
4550         }
4551         
4552         return child_height;
4553     },
4554     toggleHeaderInput : function(is_edit)
4555     {
4556         if (!this.editableTitle) {
4557             return; // not editable.
4558         }
4559         if (is_edit && this.is_header_editing) {
4560             return; // already editing..
4561         }
4562         if (is_edit) {
4563     
4564             this.headerEditEl.dom.value = this.title;
4565             this.headerEditEl.removeClass('d-none');
4566             this.headerEditEl.dom.focus();
4567             this.titleEl.addClass('d-none');
4568             
4569             this.is_header_editing = true;
4570             return
4571         }
4572         // flip back to not editing.
4573         this.title = this.headerEditEl.dom.value;
4574         this.headerEditEl.addClass('d-none');
4575         this.titleEl.removeClass('d-none');
4576         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4577         this.is_header_editing = false;
4578         this.fireEvent('titlechanged', this, this.title);
4579     
4580             
4581         
4582     }
4583
4584 });
4585
4586
4587 Roo.apply(Roo.bootstrap.Modal,  {
4588     /**
4589          * Button config that displays a single OK button
4590          * @type Object
4591          */
4592         OK :  [{
4593             name : 'ok',
4594             weight : 'primary',
4595             html : 'OK'
4596         }],
4597         /**
4598          * Button config that displays Yes and No buttons
4599          * @type Object
4600          */
4601         YESNO : [
4602             {
4603                 name  : 'no',
4604                 html : 'No'
4605             },
4606             {
4607                 name  :'yes',
4608                 weight : 'primary',
4609                 html : 'Yes'
4610             }
4611         ],
4612
4613         /**
4614          * Button config that displays OK and Cancel buttons
4615          * @type Object
4616          */
4617         OKCANCEL : [
4618             {
4619                name : 'cancel',
4620                 html : 'Cancel'
4621             },
4622             {
4623                 name : 'ok',
4624                 weight : 'primary',
4625                 html : 'OK'
4626             }
4627         ],
4628         /**
4629          * Button config that displays Yes, No and Cancel buttons
4630          * @type Object
4631          */
4632         YESNOCANCEL : [
4633             {
4634                 name : 'yes',
4635                 weight : 'primary',
4636                 html : 'Yes'
4637             },
4638             {
4639                 name : 'no',
4640                 html : 'No'
4641             },
4642             {
4643                 name : 'cancel',
4644                 html : 'Cancel'
4645             }
4646         ],
4647         
4648         zIndex : 10001
4649 });
4650
4651 /*
4652  * - LGPL
4653  *
4654  * messagebox - can be used as a replace
4655  * 
4656  */
4657 /**
4658  * @class Roo.MessageBox
4659  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4660  * Example usage:
4661  *<pre><code>
4662 // Basic alert:
4663 Roo.Msg.alert('Status', 'Changes saved successfully.');
4664
4665 // Prompt for user data:
4666 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4667     if (btn == 'ok'){
4668         // process text value...
4669     }
4670 });
4671
4672 // Show a dialog using config options:
4673 Roo.Msg.show({
4674    title:'Save Changes?',
4675    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4676    buttons: Roo.Msg.YESNOCANCEL,
4677    fn: processResult,
4678    animEl: 'elId'
4679 });
4680 </code></pre>
4681  * @singleton
4682  */
4683 Roo.bootstrap.MessageBox = function(){
4684     var dlg, opt, mask, waitTimer;
4685     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4686     var buttons, activeTextEl, bwidth;
4687
4688     
4689     // private
4690     var handleButton = function(button){
4691         dlg.hide();
4692         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4693     };
4694
4695     // private
4696     var handleHide = function(){
4697         if(opt && opt.cls){
4698             dlg.el.removeClass(opt.cls);
4699         }
4700         //if(waitTimer){
4701         //    Roo.TaskMgr.stop(waitTimer);
4702         //    waitTimer = null;
4703         //}
4704     };
4705
4706     // private
4707     var updateButtons = function(b){
4708         var width = 0;
4709         if(!b){
4710             buttons["ok"].hide();
4711             buttons["cancel"].hide();
4712             buttons["yes"].hide();
4713             buttons["no"].hide();
4714             dlg.footerEl.hide();
4715             
4716             return width;
4717         }
4718         dlg.footerEl.show();
4719         for(var k in buttons){
4720             if(typeof buttons[k] != "function"){
4721                 if(b[k]){
4722                     buttons[k].show();
4723                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4724                     width += buttons[k].el.getWidth()+15;
4725                 }else{
4726                     buttons[k].hide();
4727                 }
4728             }
4729         }
4730         return width;
4731     };
4732
4733     // private
4734     var handleEsc = function(d, k, e){
4735         if(opt && opt.closable !== false){
4736             dlg.hide();
4737         }
4738         if(e){
4739             e.stopEvent();
4740         }
4741     };
4742
4743     return {
4744         /**
4745          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4746          * @return {Roo.BasicDialog} The BasicDialog element
4747          */
4748         getDialog : function(){
4749            if(!dlg){
4750                 dlg = new Roo.bootstrap.Modal( {
4751                     //draggable: true,
4752                     //resizable:false,
4753                     //constraintoviewport:false,
4754                     //fixedcenter:true,
4755                     //collapsible : false,
4756                     //shim:true,
4757                     //modal: true,
4758                 //    width: 'auto',
4759                   //  height:100,
4760                     //buttonAlign:"center",
4761                     closeClick : function(){
4762                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4763                             handleButton("no");
4764                         }else{
4765                             handleButton("cancel");
4766                         }
4767                     }
4768                 });
4769                 dlg.render();
4770                 dlg.on("hide", handleHide);
4771                 mask = dlg.mask;
4772                 //dlg.addKeyListener(27, handleEsc);
4773                 buttons = {};
4774                 this.buttons = buttons;
4775                 var bt = this.buttonText;
4776                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4777                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4778                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4779                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4780                 //Roo.log(buttons);
4781                 bodyEl = dlg.bodyEl.createChild({
4782
4783                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4784                         '<textarea class="roo-mb-textarea"></textarea>' +
4785                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4786                 });
4787                 msgEl = bodyEl.dom.firstChild;
4788                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4789                 textboxEl.enableDisplayMode();
4790                 textboxEl.addKeyListener([10,13], function(){
4791                     if(dlg.isVisible() && opt && opt.buttons){
4792                         if(opt.buttons.ok){
4793                             handleButton("ok");
4794                         }else if(opt.buttons.yes){
4795                             handleButton("yes");
4796                         }
4797                     }
4798                 });
4799                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4800                 textareaEl.enableDisplayMode();
4801                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4802                 progressEl.enableDisplayMode();
4803                 
4804                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4805                 var pf = progressEl.dom.firstChild;
4806                 if (pf) {
4807                     pp = Roo.get(pf.firstChild);
4808                     pp.setHeight(pf.offsetHeight);
4809                 }
4810                 
4811             }
4812             return dlg;
4813         },
4814
4815         /**
4816          * Updates the message box body text
4817          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4818          * the XHTML-compliant non-breaking space character '&amp;#160;')
4819          * @return {Roo.MessageBox} This message box
4820          */
4821         updateText : function(text)
4822         {
4823             if(!dlg.isVisible() && !opt.width){
4824                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4825                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4826             }
4827             msgEl.innerHTML = text || '&#160;';
4828       
4829             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4830             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4831             var w = Math.max(
4832                     Math.min(opt.width || cw , this.maxWidth), 
4833                     Math.max(opt.minWidth || this.minWidth, bwidth)
4834             );
4835             if(opt.prompt){
4836                 activeTextEl.setWidth(w);
4837             }
4838             if(dlg.isVisible()){
4839                 dlg.fixedcenter = false;
4840             }
4841             // to big, make it scroll. = But as usual stupid IE does not support
4842             // !important..
4843             
4844             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4845                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4846                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4847             } else {
4848                 bodyEl.dom.style.height = '';
4849                 bodyEl.dom.style.overflowY = '';
4850             }
4851             if (cw > w) {
4852                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4853             } else {
4854                 bodyEl.dom.style.overflowX = '';
4855             }
4856             
4857             dlg.setContentSize(w, bodyEl.getHeight());
4858             if(dlg.isVisible()){
4859                 dlg.fixedcenter = true;
4860             }
4861             return this;
4862         },
4863
4864         /**
4865          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4866          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4867          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4868          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4869          * @return {Roo.MessageBox} This message box
4870          */
4871         updateProgress : function(value, text){
4872             if(text){
4873                 this.updateText(text);
4874             }
4875             
4876             if (pp) { // weird bug on my firefox - for some reason this is not defined
4877                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4878                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4879             }
4880             return this;
4881         },        
4882
4883         /**
4884          * Returns true if the message box is currently displayed
4885          * @return {Boolean} True if the message box is visible, else false
4886          */
4887         isVisible : function(){
4888             return dlg && dlg.isVisible();  
4889         },
4890
4891         /**
4892          * Hides the message box if it is displayed
4893          */
4894         hide : function(){
4895             if(this.isVisible()){
4896                 dlg.hide();
4897             }  
4898         },
4899
4900         /**
4901          * Displays a new message box, or reinitializes an existing message box, based on the config options
4902          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4903          * The following config object properties are supported:
4904          * <pre>
4905 Property    Type             Description
4906 ----------  ---------------  ------------------------------------------------------------------------------------
4907 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4908                                    closes (defaults to undefined)
4909 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4910                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4911 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4912                                    progress and wait dialogs will ignore this property and always hide the
4913                                    close button as they can only be closed programmatically.
4914 cls               String           A custom CSS class to apply to the message box element
4915 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4916                                    displayed (defaults to 75)
4917 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4918                                    function will be btn (the name of the button that was clicked, if applicable,
4919                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4920                                    Progress and wait dialogs will ignore this option since they do not respond to
4921                                    user actions and can only be closed programmatically, so any required function
4922                                    should be called by the same code after it closes the dialog.
4923 icon              String           A CSS class that provides a background image to be used as an icon for
4924                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4925 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4926 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4927 modal             Boolean          False to allow user interaction with the page while the message box is
4928                                    displayed (defaults to true)
4929 msg               String           A string that will replace the existing message box body text (defaults
4930                                    to the XHTML-compliant non-breaking space character '&#160;')
4931 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4932 progress          Boolean          True to display a progress bar (defaults to false)
4933 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4934 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4935 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4936 title             String           The title text
4937 value             String           The string value to set into the active textbox element if displayed
4938 wait              Boolean          True to display a progress bar (defaults to false)
4939 width             Number           The width of the dialog in pixels
4940 </pre>
4941          *
4942          * Example usage:
4943          * <pre><code>
4944 Roo.Msg.show({
4945    title: 'Address',
4946    msg: 'Please enter your address:',
4947    width: 300,
4948    buttons: Roo.MessageBox.OKCANCEL,
4949    multiline: true,
4950    fn: saveAddress,
4951    animEl: 'addAddressBtn'
4952 });
4953 </code></pre>
4954          * @param {Object} config Configuration options
4955          * @return {Roo.MessageBox} This message box
4956          */
4957         show : function(options)
4958         {
4959             
4960             // this causes nightmares if you show one dialog after another
4961             // especially on callbacks..
4962              
4963             if(this.isVisible()){
4964                 
4965                 this.hide();
4966                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4967                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4968                 Roo.log("New Dialog Message:" +  options.msg )
4969                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4970                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4971                 
4972             }
4973             var d = this.getDialog();
4974             opt = options;
4975             d.setTitle(opt.title || "&#160;");
4976             d.closeEl.setDisplayed(opt.closable !== false);
4977             activeTextEl = textboxEl;
4978             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4979             if(opt.prompt){
4980                 if(opt.multiline){
4981                     textboxEl.hide();
4982                     textareaEl.show();
4983                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4984                         opt.multiline : this.defaultTextHeight);
4985                     activeTextEl = textareaEl;
4986                 }else{
4987                     textboxEl.show();
4988                     textareaEl.hide();
4989                 }
4990             }else{
4991                 textboxEl.hide();
4992                 textareaEl.hide();
4993             }
4994             progressEl.setDisplayed(opt.progress === true);
4995             if (opt.progress) {
4996                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4997             }
4998             this.updateProgress(0);
4999             activeTextEl.dom.value = opt.value || "";
5000             if(opt.prompt){
5001                 dlg.setDefaultButton(activeTextEl);
5002             }else{
5003                 var bs = opt.buttons;
5004                 var db = null;
5005                 if(bs && bs.ok){
5006                     db = buttons["ok"];
5007                 }else if(bs && bs.yes){
5008                     db = buttons["yes"];
5009                 }
5010                 dlg.setDefaultButton(db);
5011             }
5012             bwidth = updateButtons(opt.buttons);
5013             this.updateText(opt.msg);
5014             if(opt.cls){
5015                 d.el.addClass(opt.cls);
5016             }
5017             d.proxyDrag = opt.proxyDrag === true;
5018             d.modal = opt.modal !== false;
5019             d.mask = opt.modal !== false ? mask : false;
5020             if(!d.isVisible()){
5021                 // force it to the end of the z-index stack so it gets a cursor in FF
5022                 document.body.appendChild(dlg.el.dom);
5023                 d.animateTarget = null;
5024                 d.show(options.animEl);
5025             }
5026             return this;
5027         },
5028
5029         /**
5030          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5031          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5032          * and closing the message box when the process is complete.
5033          * @param {String} title The title bar text
5034          * @param {String} msg The message box body text
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         progress : function(title, msg){
5038             this.show({
5039                 title : title,
5040                 msg : msg,
5041                 buttons: false,
5042                 progress:true,
5043                 closable:false,
5044                 minWidth: this.minProgressWidth,
5045                 modal : true
5046             });
5047             return this;
5048         },
5049
5050         /**
5051          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5052          * If a callback function is passed it will be called after the user clicks the button, and the
5053          * id of the button that was clicked will be passed as the only parameter to the callback
5054          * (could also be the top-right close button).
5055          * @param {String} title The title bar text
5056          * @param {String} msg The message box body text
5057          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5058          * @param {Object} scope (optional) The scope of the callback function
5059          * @return {Roo.MessageBox} This message box
5060          */
5061         alert : function(title, msg, fn, scope)
5062         {
5063             this.show({
5064                 title : title,
5065                 msg : msg,
5066                 buttons: this.OK,
5067                 fn: fn,
5068                 closable : false,
5069                 scope : scope,
5070                 modal : true
5071             });
5072             return this;
5073         },
5074
5075         /**
5076          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5077          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5078          * You are responsible for closing the message box when the process is complete.
5079          * @param {String} msg The message box body text
5080          * @param {String} title (optional) The title bar text
5081          * @return {Roo.MessageBox} This message box
5082          */
5083         wait : function(msg, title){
5084             this.show({
5085                 title : title,
5086                 msg : msg,
5087                 buttons: false,
5088                 closable:false,
5089                 progress:true,
5090                 modal:true,
5091                 width:300,
5092                 wait:true
5093             });
5094             waitTimer = Roo.TaskMgr.start({
5095                 run: function(i){
5096                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5097                 },
5098                 interval: 1000
5099             });
5100             return this;
5101         },
5102
5103         /**
5104          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5105          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5106          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @return {Roo.MessageBox} This message box
5112          */
5113         confirm : function(title, msg, fn, scope){
5114             this.show({
5115                 title : title,
5116                 msg : msg,
5117                 buttons: this.YESNO,
5118                 fn: fn,
5119                 scope : scope,
5120                 modal : true
5121             });
5122             return this;
5123         },
5124
5125         /**
5126          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5127          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5128          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5129          * (could also be the top-right close button) and the text that was entered will be passed as the two
5130          * parameters to the callback.
5131          * @param {String} title The title bar text
5132          * @param {String} msg The message box body text
5133          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5134          * @param {Object} scope (optional) The scope of the callback function
5135          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5136          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5137          * @return {Roo.MessageBox} This message box
5138          */
5139         prompt : function(title, msg, fn, scope, multiline){
5140             this.show({
5141                 title : title,
5142                 msg : msg,
5143                 buttons: this.OKCANCEL,
5144                 fn: fn,
5145                 minWidth:250,
5146                 scope : scope,
5147                 prompt:true,
5148                 multiline: multiline,
5149                 modal : true
5150             });
5151             return this;
5152         },
5153
5154         /**
5155          * Button config that displays a single OK button
5156          * @type Object
5157          */
5158         OK : {ok:true},
5159         /**
5160          * Button config that displays Yes and No buttons
5161          * @type Object
5162          */
5163         YESNO : {yes:true, no:true},
5164         /**
5165          * Button config that displays OK and Cancel buttons
5166          * @type Object
5167          */
5168         OKCANCEL : {ok:true, cancel:true},
5169         /**
5170          * Button config that displays Yes, No and Cancel buttons
5171          * @type Object
5172          */
5173         YESNOCANCEL : {yes:true, no:true, cancel:true},
5174
5175         /**
5176          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5177          * @type Number
5178          */
5179         defaultTextHeight : 75,
5180         /**
5181          * The maximum width in pixels of the message box (defaults to 600)
5182          * @type Number
5183          */
5184         maxWidth : 600,
5185         /**
5186          * The minimum width in pixels of the message box (defaults to 100)
5187          * @type Number
5188          */
5189         minWidth : 100,
5190         /**
5191          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5192          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5193          * @type Number
5194          */
5195         minProgressWidth : 250,
5196         /**
5197          * An object containing the default button text strings that can be overriden for localized language support.
5198          * Supported properties are: ok, cancel, yes and no.
5199          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5200          * @type Object
5201          */
5202         buttonText : {
5203             ok : "OK",
5204             cancel : "Cancel",
5205             yes : "Yes",
5206             no : "No"
5207         }
5208     };
5209 }();
5210
5211 /**
5212  * Shorthand for {@link Roo.MessageBox}
5213  */
5214 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5215 Roo.Msg = Roo.Msg || Roo.MessageBox;
5216 /*
5217  * - LGPL
5218  *
5219  * navbar
5220  * 
5221  */
5222
5223 /**
5224  * @class Roo.bootstrap.Navbar
5225  * @extends Roo.bootstrap.Component
5226  * Bootstrap Navbar class
5227
5228  * @constructor
5229  * Create a new Navbar
5230  * @param {Object} config The config object
5231  */
5232
5233
5234 Roo.bootstrap.Navbar = function(config){
5235     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5236     this.addEvents({
5237         // raw events
5238         /**
5239          * @event beforetoggle
5240          * Fire before toggle the menu
5241          * @param {Roo.EventObject} e
5242          */
5243         "beforetoggle" : true
5244     });
5245 };
5246
5247 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5248     
5249     
5250    
5251     // private
5252     navItems : false,
5253     loadMask : false,
5254     
5255     
5256     getAutoCreate : function(){
5257         
5258         
5259         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5260         
5261     },
5262     
5263     initEvents :function ()
5264     {
5265         //Roo.log(this.el.select('.navbar-toggle',true));
5266         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5267         
5268         var mark = {
5269             tag: "div",
5270             cls:"x-dlg-mask"
5271         };
5272         
5273         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5274         
5275         var size = this.el.getSize();
5276         this.maskEl.setSize(size.width, size.height);
5277         this.maskEl.enableDisplayMode("block");
5278         this.maskEl.hide();
5279         
5280         if(this.loadMask){
5281             this.maskEl.show();
5282         }
5283     },
5284     
5285     
5286     getChildContainer : function()
5287     {
5288         if (this.el && this.el.select('.collapse').getCount()) {
5289             return this.el.select('.collapse',true).first();
5290         }
5291         
5292         return this.el;
5293     },
5294     
5295     mask : function()
5296     {
5297         this.maskEl.show();
5298     },
5299     
5300     unmask : function()
5301     {
5302         this.maskEl.hide();
5303     },
5304     onToggle : function()
5305     {
5306         
5307         if(this.fireEvent('beforetoggle', this) === false){
5308             return;
5309         }
5310         var ce = this.el.select('.navbar-collapse',true).first();
5311       
5312         if (!ce.hasClass('show')) {
5313            this.expand();
5314         } else {
5315             this.collapse();
5316         }
5317         
5318         
5319     
5320     },
5321     /**
5322      * Expand the navbar pulldown 
5323      */
5324     expand : function ()
5325     {
5326        
5327         var ce = this.el.select('.navbar-collapse',true).first();
5328         if (ce.hasClass('collapsing')) {
5329             return;
5330         }
5331         ce.dom.style.height = '';
5332                // show it...
5333         ce.addClass('in'); // old...
5334         ce.removeClass('collapse');
5335         ce.addClass('show');
5336         var h = ce.getHeight();
5337         Roo.log(h);
5338         ce.removeClass('show');
5339         // at this point we should be able to see it..
5340         ce.addClass('collapsing');
5341         
5342         ce.setHeight(0); // resize it ...
5343         ce.on('transitionend', function() {
5344             //Roo.log('done transition');
5345             ce.removeClass('collapsing');
5346             ce.addClass('show');
5347             ce.removeClass('collapse');
5348
5349             ce.dom.style.height = '';
5350         }, this, { single: true} );
5351         ce.setHeight(h);
5352         ce.dom.scrollTop = 0;
5353     },
5354     /**
5355      * Collapse the navbar pulldown 
5356      */
5357     collapse : function()
5358     {
5359          var ce = this.el.select('.navbar-collapse',true).first();
5360        
5361         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5362             // it's collapsed or collapsing..
5363             return;
5364         }
5365         ce.removeClass('in'); // old...
5366         ce.setHeight(ce.getHeight());
5367         ce.removeClass('show');
5368         ce.addClass('collapsing');
5369         
5370         ce.on('transitionend', function() {
5371             ce.dom.style.height = '';
5372             ce.removeClass('collapsing');
5373             ce.addClass('collapse');
5374         }, this, { single: true} );
5375         ce.setHeight(0);
5376     }
5377     
5378     
5379     
5380 });
5381
5382
5383
5384  
5385
5386  /*
5387  * - LGPL
5388  *
5389  * navbar
5390  * 
5391  */
5392
5393 /**
5394  * @class Roo.bootstrap.NavSimplebar
5395  * @extends Roo.bootstrap.Navbar
5396  * Bootstrap Sidebar class
5397  *
5398  * @cfg {Boolean} inverse is inverted color
5399  * 
5400  * @cfg {String} type (nav | pills | tabs)
5401  * @cfg {Boolean} arrangement stacked | justified
5402  * @cfg {String} align (left | right) alignment
5403  * 
5404  * @cfg {Boolean} main (true|false) main nav bar? default false
5405  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5406  * 
5407  * @cfg {String} tag (header|footer|nav|div) default is nav 
5408
5409  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5410  * 
5411  * 
5412  * @constructor
5413  * Create a new Sidebar
5414  * @param {Object} config The config object
5415  */
5416
5417
5418 Roo.bootstrap.NavSimplebar = function(config){
5419     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5420 };
5421
5422 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5423     
5424     inverse: false,
5425     
5426     type: false,
5427     arrangement: '',
5428     align : false,
5429     
5430     weight : 'light',
5431     
5432     main : false,
5433     
5434     
5435     tag : false,
5436     
5437     
5438     getAutoCreate : function(){
5439         
5440         
5441         var cfg = {
5442             tag : this.tag || 'div',
5443             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5444         };
5445         if (['light','white'].indexOf(this.weight) > -1) {
5446             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5447         }
5448         cfg.cls += ' bg-' + this.weight;
5449         
5450         if (this.inverse) {
5451             cfg.cls += ' navbar-inverse';
5452             
5453         }
5454         
5455         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5456         
5457         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5458             return cfg;
5459         }
5460         
5461         
5462     
5463         
5464         cfg.cn = [
5465             {
5466                 cls: 'nav nav-' + this.xtype,
5467                 tag : 'ul'
5468             }
5469         ];
5470         
5471          
5472         this.type = this.type || 'nav';
5473         if (['tabs','pills'].indexOf(this.type) != -1) {
5474             cfg.cn[0].cls += ' nav-' + this.type
5475         
5476         
5477         } else {
5478             if (this.type!=='nav') {
5479                 Roo.log('nav type must be nav/tabs/pills')
5480             }
5481             cfg.cn[0].cls += ' navbar-nav'
5482         }
5483         
5484         
5485         
5486         
5487         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5488             cfg.cn[0].cls += ' nav-' + this.arrangement;
5489         }
5490         
5491         
5492         if (this.align === 'right') {
5493             cfg.cn[0].cls += ' navbar-right';
5494         }
5495         
5496         
5497         
5498         
5499         return cfg;
5500     
5501         
5502     }
5503     
5504     
5505     
5506 });
5507
5508
5509
5510  
5511
5512  
5513        /*
5514  * - LGPL
5515  *
5516  * navbar
5517  * navbar-fixed-top
5518  * navbar-expand-md  fixed-top 
5519  */
5520
5521 /**
5522  * @class Roo.bootstrap.NavHeaderbar
5523  * @extends Roo.bootstrap.NavSimplebar
5524  * Bootstrap Sidebar class
5525  *
5526  * @cfg {String} brand what is brand
5527  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5528  * @cfg {String} brand_href href of the brand
5529  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5530  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5531  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5532  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5533  * 
5534  * @constructor
5535  * Create a new Sidebar
5536  * @param {Object} config The config object
5537  */
5538
5539
5540 Roo.bootstrap.NavHeaderbar = function(config){
5541     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5542       
5543 };
5544
5545 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5546     
5547     position: '',
5548     brand: '',
5549     brand_href: false,
5550     srButton : true,
5551     autohide : false,
5552     desktopCenter : false,
5553    
5554     
5555     getAutoCreate : function(){
5556         
5557         var   cfg = {
5558             tag: this.nav || 'nav',
5559             cls: 'navbar navbar-expand-md',
5560             role: 'navigation',
5561             cn: []
5562         };
5563         
5564         var cn = cfg.cn;
5565         if (this.desktopCenter) {
5566             cn.push({cls : 'container', cn : []});
5567             cn = cn[0].cn;
5568         }
5569         
5570         if(this.srButton){
5571             var btn = {
5572                 tag: 'button',
5573                 type: 'button',
5574                 cls: 'navbar-toggle navbar-toggler',
5575                 'data-toggle': 'collapse',
5576                 cn: [
5577                     {
5578                         tag: 'span',
5579                         cls: 'sr-only',
5580                         html: 'Toggle navigation'
5581                     },
5582                     {
5583                         tag: 'span',
5584                         cls: 'icon-bar navbar-toggler-icon'
5585                     },
5586                     {
5587                         tag: 'span',
5588                         cls: 'icon-bar'
5589                     },
5590                     {
5591                         tag: 'span',
5592                         cls: 'icon-bar'
5593                     }
5594                 ]
5595             };
5596             
5597             cn.push( Roo.bootstrap.version == 4 ? btn : {
5598                 tag: 'div',
5599                 cls: 'navbar-header',
5600                 cn: [
5601                     btn
5602                 ]
5603             });
5604         }
5605         
5606         cn.push({
5607             tag: 'div',
5608             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5609             cn : []
5610         });
5611         
5612         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5613         
5614         if (['light','white'].indexOf(this.weight) > -1) {
5615             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5616         }
5617         cfg.cls += ' bg-' + this.weight;
5618         
5619         
5620         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5621             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5622             
5623             // tag can override this..
5624             
5625             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5626         }
5627         
5628         if (this.brand !== '') {
5629             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5630             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5631                 tag: 'a',
5632                 href: this.brand_href ? this.brand_href : '#',
5633                 cls: 'navbar-brand',
5634                 cn: [
5635                 this.brand
5636                 ]
5637             });
5638         }
5639         
5640         if(this.main){
5641             cfg.cls += ' main-nav';
5642         }
5643         
5644         
5645         return cfg;
5646
5647         
5648     },
5649     getHeaderChildContainer : function()
5650     {
5651         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5652             return this.el.select('.navbar-header',true).first();
5653         }
5654         
5655         return this.getChildContainer();
5656     },
5657     
5658     getChildContainer : function()
5659     {
5660          
5661         return this.el.select('.roo-navbar-collapse',true).first();
5662          
5663         
5664     },
5665     
5666     initEvents : function()
5667     {
5668         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5669         
5670         if (this.autohide) {
5671             
5672             var prevScroll = 0;
5673             var ft = this.el;
5674             
5675             Roo.get(document).on('scroll',function(e) {
5676                 var ns = Roo.get(document).getScroll().top;
5677                 var os = prevScroll;
5678                 prevScroll = ns;
5679                 
5680                 if(ns > os){
5681                     ft.removeClass('slideDown');
5682                     ft.addClass('slideUp');
5683                     return;
5684                 }
5685                 ft.removeClass('slideUp');
5686                 ft.addClass('slideDown');
5687                  
5688               
5689           },this);
5690         }
5691     }    
5692     
5693 });
5694
5695
5696
5697  
5698
5699  /*
5700  * - LGPL
5701  *
5702  * navbar
5703  * 
5704  */
5705
5706 /**
5707  * @class Roo.bootstrap.NavSidebar
5708  * @extends Roo.bootstrap.Navbar
5709  * Bootstrap Sidebar class
5710  * 
5711  * @constructor
5712  * Create a new Sidebar
5713  * @param {Object} config The config object
5714  */
5715
5716
5717 Roo.bootstrap.NavSidebar = function(config){
5718     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5719 };
5720
5721 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5722     
5723     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5724     
5725     getAutoCreate : function(){
5726         
5727         
5728         return  {
5729             tag: 'div',
5730             cls: 'sidebar sidebar-nav'
5731         };
5732     
5733         
5734     }
5735     
5736     
5737     
5738 });
5739
5740
5741
5742  
5743
5744  /*
5745  * - LGPL
5746  *
5747  * nav group
5748  * 
5749  */
5750
5751 /**
5752  * @class Roo.bootstrap.NavGroup
5753  * @extends Roo.bootstrap.Component
5754  * Bootstrap NavGroup class
5755  * @cfg {String} align (left|right)
5756  * @cfg {Boolean} inverse
5757  * @cfg {String} type (nav|pills|tab) default nav
5758  * @cfg {String} navId - reference Id for navbar.
5759  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5760  * 
5761  * @constructor
5762  * Create a new nav group
5763  * @param {Object} config The config object
5764  */
5765
5766 Roo.bootstrap.NavGroup = function(config){
5767     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5768     this.navItems = [];
5769    
5770     Roo.bootstrap.NavGroup.register(this);
5771      this.addEvents({
5772         /**
5773              * @event changed
5774              * Fires when the active item changes
5775              * @param {Roo.bootstrap.NavGroup} this
5776              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5777              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5778          */
5779         'changed': true
5780      });
5781     
5782 };
5783
5784 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5785     
5786     align: '',
5787     inverse: false,
5788     form: false,
5789     type: 'nav',
5790     navId : '',
5791     // private
5792     pilltype : true,
5793     
5794     navItems : false, 
5795     
5796     getAutoCreate : function()
5797     {
5798         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5799         
5800         cfg = {
5801             tag : 'ul',
5802             cls: 'nav' 
5803         };
5804         if (Roo.bootstrap.version == 4) {
5805             if (['tabs','pills'].indexOf(this.type) != -1) {
5806                 cfg.cls += ' nav-' + this.type; 
5807             } else {
5808                 // trying to remove so header bar can right align top?
5809                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5810                     // do not use on header bar... 
5811                     cfg.cls += ' navbar-nav';
5812                 }
5813             }
5814             
5815         } else {
5816             if (['tabs','pills'].indexOf(this.type) != -1) {
5817                 cfg.cls += ' nav-' + this.type
5818             } else {
5819                 if (this.type !== 'nav') {
5820                     Roo.log('nav type must be nav/tabs/pills')
5821                 }
5822                 cfg.cls += ' navbar-nav'
5823             }
5824         }
5825         
5826         if (this.parent() && this.parent().sidebar) {
5827             cfg = {
5828                 tag: 'ul',
5829                 cls: 'dashboard-menu sidebar-menu'
5830             };
5831             
5832             return cfg;
5833         }
5834         
5835         if (this.form === true) {
5836             cfg = {
5837                 tag: 'form',
5838                 cls: 'navbar-form form-inline'
5839             };
5840             //nav navbar-right ml-md-auto
5841             if (this.align === 'right') {
5842                 cfg.cls += ' navbar-right ml-md-auto';
5843             } else {
5844                 cfg.cls += ' navbar-left';
5845             }
5846         }
5847         
5848         if (this.align === 'right') {
5849             cfg.cls += ' navbar-right ml-md-auto';
5850         } else {
5851             cfg.cls += ' mr-auto';
5852         }
5853         
5854         if (this.inverse) {
5855             cfg.cls += ' navbar-inverse';
5856             
5857         }
5858         
5859         
5860         return cfg;
5861     },
5862     /**
5863     * sets the active Navigation item
5864     * @param {Roo.bootstrap.NavItem} the new current navitem
5865     */
5866     setActiveItem : function(item)
5867     {
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             if (v == item) {
5871                 return ;
5872             }
5873             if (v.isActive()) {
5874                 v.setActive(false, true);
5875                 prev = v;
5876                 
5877             }
5878             
5879         });
5880
5881         item.setActive(true, true);
5882         this.fireEvent('changed', this, item, prev);
5883         
5884         
5885     },
5886     /**
5887     * gets the active Navigation item
5888     * @return {Roo.bootstrap.NavItem} the current navitem
5889     */
5890     getActive : function()
5891     {
5892         
5893         var prev = false;
5894         Roo.each(this.navItems, function(v){
5895             
5896             if (v.isActive()) {
5897                 prev = v;
5898                 
5899             }
5900             
5901         });
5902         return prev;
5903     },
5904     
5905     indexOfNav : function()
5906     {
5907         
5908         var prev = false;
5909         Roo.each(this.navItems, function(v,i){
5910             
5911             if (v.isActive()) {
5912                 prev = i;
5913                 
5914             }
5915             
5916         });
5917         return prev;
5918     },
5919     /**
5920     * adds a Navigation item
5921     * @param {Roo.bootstrap.NavItem} the navitem to add
5922     */
5923     addItem : function(cfg)
5924     {
5925         if (this.form && Roo.bootstrap.version == 4) {
5926             cfg.tag = 'div';
5927         }
5928         var cn = new Roo.bootstrap.NavItem(cfg);
5929         this.register(cn);
5930         cn.parentId = this.id;
5931         cn.onRender(this.el, null);
5932         return cn;
5933     },
5934     /**
5935     * register a Navigation item
5936     * @param {Roo.bootstrap.NavItem} the navitem to add
5937     */
5938     register : function(item)
5939     {
5940         this.navItems.push( item);
5941         item.navId = this.navId;
5942     
5943     },
5944     
5945     /**
5946     * clear all the Navigation item
5947     */
5948    
5949     clearAll : function()
5950     {
5951         this.navItems = [];
5952         this.el.dom.innerHTML = '';
5953     },
5954     
5955     getNavItem: function(tabId)
5956     {
5957         var ret = false;
5958         Roo.each(this.navItems, function(e) {
5959             if (e.tabId == tabId) {
5960                ret =  e;
5961                return false;
5962             }
5963             return true;
5964             
5965         });
5966         return ret;
5967     },
5968     
5969     setActiveNext : function()
5970     {
5971         var i = this.indexOfNav(this.getActive());
5972         if (i > this.navItems.length) {
5973             return;
5974         }
5975         this.setActiveItem(this.navItems[i+1]);
5976     },
5977     setActivePrev : function()
5978     {
5979         var i = this.indexOfNav(this.getActive());
5980         if (i  < 1) {
5981             return;
5982         }
5983         this.setActiveItem(this.navItems[i-1]);
5984     },
5985     clearWasActive : function(except) {
5986         Roo.each(this.navItems, function(e) {
5987             if (e.tabId != except.tabId && e.was_active) {
5988                e.was_active = false;
5989                return false;
5990             }
5991             return true;
5992             
5993         });
5994     },
5995     getWasActive : function ()
5996     {
5997         var r = false;
5998         Roo.each(this.navItems, function(e) {
5999             if (e.was_active) {
6000                r = e;
6001                return false;
6002             }
6003             return true;
6004             
6005         });
6006         return r;
6007     }
6008     
6009     
6010 });
6011
6012  
6013 Roo.apply(Roo.bootstrap.NavGroup, {
6014     
6015     groups: {},
6016      /**
6017     * register a Navigation Group
6018     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6019     */
6020     register : function(navgrp)
6021     {
6022         this.groups[navgrp.navId] = navgrp;
6023         
6024     },
6025     /**
6026     * fetch a Navigation Group based on the navigation ID
6027     * @param {string} the navgroup to add
6028     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6029     */
6030     get: function(navId) {
6031         if (typeof(this.groups[navId]) == 'undefined') {
6032             return false;
6033             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6034         }
6035         return this.groups[navId] ;
6036     }
6037     
6038     
6039     
6040 });
6041
6042  /*
6043  * - LGPL
6044  *
6045  * row
6046  * 
6047  */
6048
6049 /**
6050  * @class Roo.bootstrap.NavItem
6051  * @extends Roo.bootstrap.Component
6052  * Bootstrap Navbar.NavItem class
6053  * @cfg {String} href  link to
6054  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6055  * @cfg {Boolean} button_outline show and outlined button
6056  * @cfg {String} html content of button
6057  * @cfg {String} badge text inside badge
6058  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6059  * @cfg {String} glyphicon DEPRICATED - use fa
6060  * @cfg {String} icon DEPRICATED - use fa
6061  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6062  * @cfg {Boolean} active Is item active
6063  * @cfg {Boolean} disabled Is item disabled
6064  * @cfg {String} linkcls  Link Class
6065  * @cfg {Boolean} preventDefault (true | false) default false
6066  * @cfg {String} tabId the tab that this item activates.
6067  * @cfg {String} tagtype (a|span) render as a href or span?
6068  * @cfg {Boolean} animateRef (true|false) link to element default false  
6069   
6070  * @constructor
6071  * Create a new Navbar Item
6072  * @param {Object} config The config object
6073  */
6074 Roo.bootstrap.NavItem = function(config){
6075     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6076     this.addEvents({
6077         // raw events
6078         /**
6079          * @event click
6080          * The raw click event for the entire grid.
6081          * @param {Roo.EventObject} e
6082          */
6083         "click" : true,
6084          /**
6085             * @event changed
6086             * Fires when the active item active state changes
6087             * @param {Roo.bootstrap.NavItem} this
6088             * @param {boolean} state the new state
6089              
6090          */
6091         'changed': true,
6092         /**
6093             * @event scrollto
6094             * Fires when scroll to element
6095             * @param {Roo.bootstrap.NavItem} this
6096             * @param {Object} options
6097             * @param {Roo.EventObject} e
6098              
6099          */
6100         'scrollto': true
6101     });
6102    
6103 };
6104
6105 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6106     
6107     href: false,
6108     html: '',
6109     badge: '',
6110     icon: false,
6111     fa : false,
6112     glyphicon: false,
6113     active: false,
6114     preventDefault : false,
6115     tabId : false,
6116     tagtype : 'a',
6117     tag: 'li',
6118     disabled : false,
6119     animateRef : false,
6120     was_active : false,
6121     button_weight : '',
6122     button_outline : false,
6123     linkcls : '',
6124     navLink: false,
6125     
6126     getAutoCreate : function(){
6127          
6128         var cfg = {
6129             tag: this.tag,
6130             cls: 'nav-item'
6131         };
6132         
6133         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6134         
6135         if (this.active) {
6136             cfg.cls +=  ' active' ;
6137         }
6138         if (this.disabled) {
6139             cfg.cls += ' disabled';
6140         }
6141         
6142         // BS4 only?
6143         if (this.button_weight.length) {
6144             cfg.tag = this.href ? 'a' : 'button';
6145             cfg.html = this.html || '';
6146             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6147             if (this.href) {
6148                 cfg.href = this.href;
6149             }
6150             if (this.fa) {
6151                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6152             }
6153             
6154             // menu .. should add dropdown-menu class - so no need for carat..
6155             
6156             if (this.badge !== '') {
6157                  
6158                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6159             }
6160             return cfg;
6161         }
6162         
6163         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6164             cfg.cn = [
6165                 {
6166                     tag: this.tagtype,
6167                     href : this.href || "#",
6168                     html: this.html || ''
6169                 }
6170             ];
6171             if (this.tagtype == 'a') {
6172                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6173         
6174             }
6175             if (this.icon) {
6176                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6177             }
6178             if (this.fa) {
6179                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6180             }
6181             if(this.glyphicon) {
6182                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6183             }
6184             
6185             if (this.menu) {
6186                 
6187                 cfg.cn[0].html += " <span class='caret'></span>";
6188              
6189             }
6190             
6191             if (this.badge !== '') {
6192                  
6193                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6194             }
6195         }
6196         
6197         
6198         
6199         return cfg;
6200     },
6201     onRender : function(ct, position)
6202     {
6203        // Roo.log("Call onRender: " + this.xtype);
6204         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6205             this.tag = 'div';
6206         }
6207         
6208         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6209         this.navLink = this.el.select('.nav-link',true).first();
6210         return ret;
6211     },
6212       
6213     
6214     initEvents: function() 
6215     {
6216         if (typeof (this.menu) != 'undefined') {
6217             this.menu.parentType = this.xtype;
6218             this.menu.triggerEl = this.el;
6219             this.menu = this.addxtype(Roo.apply({}, this.menu));
6220         }
6221         
6222         this.el.on('click', this.onClick, this);
6223         
6224         //if(this.tagtype == 'span'){
6225         //    this.el.select('span',true).on('click', this.onClick, this);
6226         //}
6227        
6228         // at this point parent should be available..
6229         this.parent().register(this);
6230     },
6231     
6232     onClick : function(e)
6233     {
6234         if (e.getTarget('.dropdown-menu-item')) {
6235             // did you click on a menu itemm.... - then don't trigger onclick..
6236             return;
6237         }
6238         
6239         if(
6240                 this.preventDefault || 
6241                 this.href == '#' 
6242         ){
6243             Roo.log("NavItem - prevent Default?");
6244             e.preventDefault();
6245         }
6246         
6247         if (this.disabled) {
6248             return;
6249         }
6250         
6251         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6252         if (tg && tg.transition) {
6253             Roo.log("waiting for the transitionend");
6254             return;
6255         }
6256         
6257         
6258         
6259         //Roo.log("fire event clicked");
6260         if(this.fireEvent('click', this, e) === false){
6261             return;
6262         };
6263         
6264         if(this.tagtype == 'span'){
6265             return;
6266         }
6267         
6268         //Roo.log(this.href);
6269         var ael = this.el.select('a',true).first();
6270         //Roo.log(ael);
6271         
6272         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6273             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6274             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6275                 return; // ignore... - it's a 'hash' to another page.
6276             }
6277             Roo.log("NavItem - prevent Default?");
6278             e.preventDefault();
6279             this.scrollToElement(e);
6280         }
6281         
6282         
6283         var p =  this.parent();
6284    
6285         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6286             if (typeof(p.setActiveItem) !== 'undefined') {
6287                 p.setActiveItem(this);
6288             }
6289         }
6290         
6291         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6292         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6293             // remove the collapsed menu expand...
6294             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6295         }
6296     },
6297     
6298     isActive: function () {
6299         return this.active
6300     },
6301     setActive : function(state, fire, is_was_active)
6302     {
6303         if (this.active && !state && this.navId) {
6304             this.was_active = true;
6305             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6306             if (nv) {
6307                 nv.clearWasActive(this);
6308             }
6309             
6310         }
6311         this.active = state;
6312         
6313         if (!state ) {
6314             this.el.removeClass('active');
6315             this.navLink ? this.navLink.removeClass('active') : false;
6316         } else if (!this.el.hasClass('active')) {
6317             
6318             this.el.addClass('active');
6319             if (Roo.bootstrap.version == 4 && this.navLink ) {
6320                 this.navLink.addClass('active');
6321             }
6322             
6323         }
6324         if (fire) {
6325             this.fireEvent('changed', this, state);
6326         }
6327         
6328         // show a panel if it's registered and related..
6329         
6330         if (!this.navId || !this.tabId || !state || is_was_active) {
6331             return;
6332         }
6333         
6334         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6335         if (!tg) {
6336             return;
6337         }
6338         var pan = tg.getPanelByName(this.tabId);
6339         if (!pan) {
6340             return;
6341         }
6342         // if we can not flip to new panel - go back to old nav highlight..
6343         if (false == tg.showPanel(pan)) {
6344             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6345             if (nv) {
6346                 var onav = nv.getWasActive();
6347                 if (onav) {
6348                     onav.setActive(true, false, true);
6349                 }
6350             }
6351             
6352         }
6353         
6354         
6355         
6356     },
6357      // this should not be here...
6358     setDisabled : function(state)
6359     {
6360         this.disabled = state;
6361         if (!state ) {
6362             this.el.removeClass('disabled');
6363         } else if (!this.el.hasClass('disabled')) {
6364             this.el.addClass('disabled');
6365         }
6366         
6367     },
6368     
6369     /**
6370      * Fetch the element to display the tooltip on.
6371      * @return {Roo.Element} defaults to this.el
6372      */
6373     tooltipEl : function()
6374     {
6375         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6376     },
6377     
6378     scrollToElement : function(e)
6379     {
6380         var c = document.body;
6381         
6382         /*
6383          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6384          */
6385         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6386             c = document.documentElement;
6387         }
6388         
6389         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6390         
6391         if(!target){
6392             return;
6393         }
6394
6395         var o = target.calcOffsetsTo(c);
6396         
6397         var options = {
6398             target : target,
6399             value : o[1]
6400         };
6401         
6402         this.fireEvent('scrollto', this, options, e);
6403         
6404         Roo.get(c).scrollTo('top', options.value, true);
6405         
6406         return;
6407     }
6408 });
6409  
6410
6411  /*
6412  * - LGPL
6413  *
6414  * sidebar item
6415  *
6416  *  li
6417  *    <span> icon </span>
6418  *    <span> text </span>
6419  *    <span>badge </span>
6420  */
6421
6422 /**
6423  * @class Roo.bootstrap.NavSidebarItem
6424  * @extends Roo.bootstrap.NavItem
6425  * Bootstrap Navbar.NavSidebarItem class
6426  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6427  * {Boolean} open is the menu open
6428  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6429  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6430  * {String} buttonSize (sm|md|lg)the extra classes for the button
6431  * {Boolean} showArrow show arrow next to the text (default true)
6432  * @constructor
6433  * Create a new Navbar Button
6434  * @param {Object} config The config object
6435  */
6436 Roo.bootstrap.NavSidebarItem = function(config){
6437     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6438     this.addEvents({
6439         // raw events
6440         /**
6441          * @event click
6442          * The raw click event for the entire grid.
6443          * @param {Roo.EventObject} e
6444          */
6445         "click" : true,
6446          /**
6447             * @event changed
6448             * Fires when the active item active state changes
6449             * @param {Roo.bootstrap.NavSidebarItem} this
6450             * @param {boolean} state the new state
6451              
6452          */
6453         'changed': true
6454     });
6455    
6456 };
6457
6458 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6459     
6460     badgeWeight : 'default',
6461     
6462     open: false,
6463     
6464     buttonView : false,
6465     
6466     buttonWeight : 'default',
6467     
6468     buttonSize : 'md',
6469     
6470     showArrow : true,
6471     
6472     getAutoCreate : function(){
6473         
6474         
6475         var a = {
6476                 tag: 'a',
6477                 href : this.href || '#',
6478                 cls: '',
6479                 html : '',
6480                 cn : []
6481         };
6482         
6483         if(this.buttonView){
6484             a = {
6485                 tag: 'button',
6486                 href : this.href || '#',
6487                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6488                 html : this.html,
6489                 cn : []
6490             };
6491         }
6492         
6493         var cfg = {
6494             tag: 'li',
6495             cls: '',
6496             cn: [ a ]
6497         };
6498         
6499         if (this.active) {
6500             cfg.cls += ' active';
6501         }
6502         
6503         if (this.disabled) {
6504             cfg.cls += ' disabled';
6505         }
6506         if (this.open) {
6507             cfg.cls += ' open x-open';
6508         }
6509         // left icon..
6510         if (this.glyphicon || this.icon) {
6511             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6512             a.cn.push({ tag : 'i', cls : c }) ;
6513         }
6514         
6515         if(!this.buttonView){
6516             var span = {
6517                 tag: 'span',
6518                 html : this.html || ''
6519             };
6520
6521             a.cn.push(span);
6522             
6523         }
6524         
6525         if (this.badge !== '') {
6526             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6527         }
6528         
6529         if (this.menu) {
6530             
6531             if(this.showArrow){
6532                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6533             }
6534             
6535             a.cls += ' dropdown-toggle treeview' ;
6536         }
6537         
6538         return cfg;
6539     },
6540     
6541     initEvents : function()
6542     { 
6543         if (typeof (this.menu) != 'undefined') {
6544             this.menu.parentType = this.xtype;
6545             this.menu.triggerEl = this.el;
6546             this.menu = this.addxtype(Roo.apply({}, this.menu));
6547         }
6548         
6549         this.el.on('click', this.onClick, this);
6550         
6551         if(this.badge !== ''){
6552             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6553         }
6554         
6555     },
6556     
6557     onClick : function(e)
6558     {
6559         if(this.disabled){
6560             e.preventDefault();
6561             return;
6562         }
6563         
6564         if(this.preventDefault){
6565             e.preventDefault();
6566         }
6567         
6568         this.fireEvent('click', this, e);
6569     },
6570     
6571     disable : function()
6572     {
6573         this.setDisabled(true);
6574     },
6575     
6576     enable : function()
6577     {
6578         this.setDisabled(false);
6579     },
6580     
6581     setDisabled : function(state)
6582     {
6583         if(this.disabled == state){
6584             return;
6585         }
6586         
6587         this.disabled = state;
6588         
6589         if (state) {
6590             this.el.addClass('disabled');
6591             return;
6592         }
6593         
6594         this.el.removeClass('disabled');
6595         
6596         return;
6597     },
6598     
6599     setActive : function(state)
6600     {
6601         if(this.active == state){
6602             return;
6603         }
6604         
6605         this.active = state;
6606         
6607         if (state) {
6608             this.el.addClass('active');
6609             return;
6610         }
6611         
6612         this.el.removeClass('active');
6613         
6614         return;
6615     },
6616     
6617     isActive: function () 
6618     {
6619         return this.active;
6620     },
6621     
6622     setBadge : function(str)
6623     {
6624         if(!this.badgeEl){
6625             return;
6626         }
6627         
6628         this.badgeEl.dom.innerHTML = str;
6629     }
6630     
6631    
6632      
6633  
6634 });
6635  
6636
6637  /*
6638  * - LGPL
6639  *
6640  *  Breadcrumb Nav
6641  * 
6642  */
6643 Roo.namespace('Roo.bootstrap.breadcrumb');
6644
6645
6646 /**
6647  * @class Roo.bootstrap.breadcrumb.Nav
6648  * @extends Roo.bootstrap.Component
6649  * Bootstrap Breadcrumb Nav Class
6650  *  
6651  * @children Roo.bootstrap.breadcrumb.Item
6652  * 
6653  * @constructor
6654  * Create a new breadcrumb.Nav
6655  * @param {Object} config The config object
6656  */
6657
6658
6659 Roo.bootstrap.breadcrumb.Nav = function(config){
6660     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6661     
6662     
6663 };
6664
6665 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6666     
6667     getAutoCreate : function()
6668     {
6669
6670         var cfg = {
6671             tag: 'nav',
6672             cn : [
6673                 {
6674                     tag : 'ol',
6675                     cls : 'breadcrumb'
6676                 }
6677             ]
6678             
6679         };
6680           
6681         return cfg;
6682     },
6683     
6684     initEvents: function()
6685     {
6686         this.olEl = this.el.select('ol',true).first();    
6687     },
6688     getChildContainer : function()
6689     {
6690         return this.olEl;  
6691     }
6692     
6693 });
6694
6695  /*
6696  * - LGPL
6697  *
6698  *  Breadcrumb Item
6699  * 
6700  */
6701
6702
6703 /**
6704  * @class Roo.bootstrap.breadcrumb.Nav
6705  * @extends Roo.bootstrap.Component
6706  * Bootstrap Breadcrumb Nav Class
6707  *  
6708  * @children Roo.bootstrap.breadcrumb.Component
6709  * @cfg {String} html the content of the link.
6710  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6711  * @cfg {Boolean} active is it active
6712
6713  * 
6714  * @constructor
6715  * Create a new breadcrumb.Nav
6716  * @param {Object} config The config object
6717  */
6718
6719 Roo.bootstrap.breadcrumb.Item = function(config){
6720     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6721     this.addEvents({
6722         // img events
6723         /**
6724          * @event click
6725          * The img click event for the img.
6726          * @param {Roo.EventObject} e
6727          */
6728         "click" : true
6729     });
6730     
6731 };
6732
6733 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6734     
6735     href: false,
6736     html : '',
6737     
6738     getAutoCreate : function()
6739     {
6740
6741         var cfg = {
6742             tag: 'li',
6743             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6744         };
6745         if (this.href !== false) {
6746             cfg.cn = [{
6747                 tag : 'a',
6748                 href : this.href,
6749                 html : this.html
6750             }];
6751         } else {
6752             cfg.html = this.html;
6753         }
6754         
6755         return cfg;
6756     },
6757     
6758     initEvents: function()
6759     {
6760         if (this.href) {
6761             this.el.select('a', true).first().on('click',this.onClick, this)
6762         }
6763         
6764     },
6765     onClick : function(e)
6766     {
6767         e.preventDefault();
6768         this.fireEvent('click',this,  e);
6769     }
6770     
6771 });
6772
6773  /*
6774  * - LGPL
6775  *
6776  * row
6777  * 
6778  */
6779
6780 /**
6781  * @class Roo.bootstrap.Row
6782  * @extends Roo.bootstrap.Component
6783  * Bootstrap Row class (contains columns...)
6784  * 
6785  * @constructor
6786  * Create a new Row
6787  * @param {Object} config The config object
6788  */
6789
6790 Roo.bootstrap.Row = function(config){
6791     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6792 };
6793
6794 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6795     
6796     getAutoCreate : function(){
6797        return {
6798             cls: 'row clearfix'
6799        };
6800     }
6801     
6802     
6803 });
6804
6805  
6806
6807  /*
6808  * - LGPL
6809  *
6810  * pagination
6811  * 
6812  */
6813
6814 /**
6815  * @class Roo.bootstrap.Pagination
6816  * @extends Roo.bootstrap.Component
6817  * Bootstrap Pagination class
6818  * @cfg {String} size xs | sm | md | lg
6819  * @cfg {Boolean} inverse false | true
6820  * 
6821  * @constructor
6822  * Create a new Pagination
6823  * @param {Object} config The config object
6824  */
6825
6826 Roo.bootstrap.Pagination = function(config){
6827     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6828 };
6829
6830 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6831     
6832     cls: false,
6833     size: false,
6834     inverse: false,
6835     
6836     getAutoCreate : function(){
6837         var cfg = {
6838             tag: 'ul',
6839                 cls: 'pagination'
6840         };
6841         if (this.inverse) {
6842             cfg.cls += ' inverse';
6843         }
6844         if (this.html) {
6845             cfg.html=this.html;
6846         }
6847         if (this.cls) {
6848             cfg.cls += " " + this.cls;
6849         }
6850         return cfg;
6851     }
6852    
6853 });
6854
6855  
6856
6857  /*
6858  * - LGPL
6859  *
6860  * Pagination item
6861  * 
6862  */
6863
6864
6865 /**
6866  * @class Roo.bootstrap.PaginationItem
6867  * @extends Roo.bootstrap.Component
6868  * Bootstrap PaginationItem class
6869  * @cfg {String} html text
6870  * @cfg {String} href the link
6871  * @cfg {Boolean} preventDefault (true | false) default true
6872  * @cfg {Boolean} active (true | false) default false
6873  * @cfg {Boolean} disabled default false
6874  * 
6875  * 
6876  * @constructor
6877  * Create a new PaginationItem
6878  * @param {Object} config The config object
6879  */
6880
6881
6882 Roo.bootstrap.PaginationItem = function(config){
6883     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6884     this.addEvents({
6885         // raw events
6886         /**
6887          * @event click
6888          * The raw click event for the entire grid.
6889          * @param {Roo.EventObject} e
6890          */
6891         "click" : true
6892     });
6893 };
6894
6895 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6896     
6897     href : false,
6898     html : false,
6899     preventDefault: true,
6900     active : false,
6901     cls : false,
6902     disabled: false,
6903     
6904     getAutoCreate : function(){
6905         var cfg= {
6906             tag: 'li',
6907             cn: [
6908                 {
6909                     tag : 'a',
6910                     href : this.href ? this.href : '#',
6911                     html : this.html ? this.html : ''
6912                 }
6913             ]
6914         };
6915         
6916         if(this.cls){
6917             cfg.cls = this.cls;
6918         }
6919         
6920         if(this.disabled){
6921             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6922         }
6923         
6924         if(this.active){
6925             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6926         }
6927         
6928         return cfg;
6929     },
6930     
6931     initEvents: function() {
6932         
6933         this.el.on('click', this.onClick, this);
6934         
6935     },
6936     onClick : function(e)
6937     {
6938         Roo.log('PaginationItem on click ');
6939         if(this.preventDefault){
6940             e.preventDefault();
6941         }
6942         
6943         if(this.disabled){
6944             return;
6945         }
6946         
6947         this.fireEvent('click', this, e);
6948     }
6949    
6950 });
6951
6952  
6953
6954  /*
6955  * - LGPL
6956  *
6957  * slider
6958  * 
6959  */
6960
6961
6962 /**
6963  * @class Roo.bootstrap.Slider
6964  * @extends Roo.bootstrap.Component
6965  * Bootstrap Slider class
6966  *    
6967  * @constructor
6968  * Create a new Slider
6969  * @param {Object} config The config object
6970  */
6971
6972 Roo.bootstrap.Slider = function(config){
6973     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6974 };
6975
6976 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6977     
6978     getAutoCreate : function(){
6979         
6980         var cfg = {
6981             tag: 'div',
6982             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6983             cn: [
6984                 {
6985                     tag: 'a',
6986                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6987                 }
6988             ]
6989         };
6990         
6991         return cfg;
6992     }
6993    
6994 });
6995
6996  /*
6997  * Based on:
6998  * Ext JS Library 1.1.1
6999  * Copyright(c) 2006-2007, Ext JS, LLC.
7000  *
7001  * Originally Released Under LGPL - original licence link has changed is not relivant.
7002  *
7003  * Fork - LGPL
7004  * <script type="text/javascript">
7005  */
7006  
7007
7008 /**
7009  * @class Roo.grid.ColumnModel
7010  * @extends Roo.util.Observable
7011  * This is the default implementation of a ColumnModel used by the Grid. It defines
7012  * the columns in the grid.
7013  * <br>Usage:<br>
7014  <pre><code>
7015  var colModel = new Roo.grid.ColumnModel([
7016         {header: "Ticker", width: 60, sortable: true, locked: true},
7017         {header: "Company Name", width: 150, sortable: true},
7018         {header: "Market Cap.", width: 100, sortable: true},
7019         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7020         {header: "Employees", width: 100, sortable: true, resizable: false}
7021  ]);
7022  </code></pre>
7023  * <p>
7024  
7025  * The config options listed for this class are options which may appear in each
7026  * individual column definition.
7027  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7028  * @constructor
7029  * @param {Object} config An Array of column config objects. See this class's
7030  * config objects for details.
7031 */
7032 Roo.grid.ColumnModel = function(config){
7033         /**
7034      * The config passed into the constructor
7035      */
7036     this.config = config;
7037     this.lookup = {};
7038
7039     // if no id, create one
7040     // if the column does not have a dataIndex mapping,
7041     // map it to the order it is in the config
7042     for(var i = 0, len = config.length; i < len; i++){
7043         var c = config[i];
7044         if(typeof c.dataIndex == "undefined"){
7045             c.dataIndex = i;
7046         }
7047         if(typeof c.renderer == "string"){
7048             c.renderer = Roo.util.Format[c.renderer];
7049         }
7050         if(typeof c.id == "undefined"){
7051             c.id = Roo.id();
7052         }
7053         if(c.editor && c.editor.xtype){
7054             c.editor  = Roo.factory(c.editor, Roo.grid);
7055         }
7056         if(c.editor && c.editor.isFormField){
7057             c.editor = new Roo.grid.GridEditor(c.editor);
7058         }
7059         this.lookup[c.id] = c;
7060     }
7061
7062     /**
7063      * The width of columns which have no width specified (defaults to 100)
7064      * @type Number
7065      */
7066     this.defaultWidth = 100;
7067
7068     /**
7069      * Default sortable of columns which have no sortable specified (defaults to false)
7070      * @type Boolean
7071      */
7072     this.defaultSortable = false;
7073
7074     this.addEvents({
7075         /**
7076              * @event widthchange
7077              * Fires when the width of a column changes.
7078              * @param {ColumnModel} this
7079              * @param {Number} columnIndex The column index
7080              * @param {Number} newWidth The new width
7081              */
7082             "widthchange": true,
7083         /**
7084              * @event headerchange
7085              * Fires when the text of a header changes.
7086              * @param {ColumnModel} this
7087              * @param {Number} columnIndex The column index
7088              * @param {Number} newText The new header text
7089              */
7090             "headerchange": true,
7091         /**
7092              * @event hiddenchange
7093              * Fires when a column is hidden or "unhidden".
7094              * @param {ColumnModel} this
7095              * @param {Number} columnIndex The column index
7096              * @param {Boolean} hidden true if hidden, false otherwise
7097              */
7098             "hiddenchange": true,
7099             /**
7100          * @event columnmoved
7101          * Fires when a column is moved.
7102          * @param {ColumnModel} this
7103          * @param {Number} oldIndex
7104          * @param {Number} newIndex
7105          */
7106         "columnmoved" : true,
7107         /**
7108          * @event columlockchange
7109          * Fires when a column's locked state is changed
7110          * @param {ColumnModel} this
7111          * @param {Number} colIndex
7112          * @param {Boolean} locked true if locked
7113          */
7114         "columnlockchange" : true
7115     });
7116     Roo.grid.ColumnModel.superclass.constructor.call(this);
7117 };
7118 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7119     /**
7120      * @cfg {String} header The header text to display in the Grid view.
7121      */
7122     /**
7123      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7124      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7125      * specified, the column's index is used as an index into the Record's data Array.
7126      */
7127     /**
7128      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7129      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7130      */
7131     /**
7132      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7133      * Defaults to the value of the {@link #defaultSortable} property.
7134      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7135      */
7136     /**
7137      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7138      */
7139     /**
7140      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7141      */
7142     /**
7143      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7144      */
7145     /**
7146      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7147      */
7148     /**
7149      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7150      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7151      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7152      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7153      */
7154        /**
7155      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7156      */
7157     /**
7158      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7159      */
7160     /**
7161      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7162      */
7163     /**
7164      * @cfg {String} cursor (Optional)
7165      */
7166     /**
7167      * @cfg {String} tooltip (Optional)
7168      */
7169     /**
7170      * @cfg {Number} xs (Optional)
7171      */
7172     /**
7173      * @cfg {Number} sm (Optional)
7174      */
7175     /**
7176      * @cfg {Number} md (Optional)
7177      */
7178     /**
7179      * @cfg {Number} lg (Optional)
7180      */
7181     /**
7182      * Returns the id of the column at the specified index.
7183      * @param {Number} index The column index
7184      * @return {String} the id
7185      */
7186     getColumnId : function(index){
7187         return this.config[index].id;
7188     },
7189
7190     /**
7191      * Returns the column for a specified id.
7192      * @param {String} id The column id
7193      * @return {Object} the column
7194      */
7195     getColumnById : function(id){
7196         return this.lookup[id];
7197     },
7198
7199     
7200     /**
7201      * Returns the column for a specified dataIndex.
7202      * @param {String} dataIndex The column dataIndex
7203      * @return {Object|Boolean} the column or false if not found
7204      */
7205     getColumnByDataIndex: function(dataIndex){
7206         var index = this.findColumnIndex(dataIndex);
7207         return index > -1 ? this.config[index] : false;
7208     },
7209     
7210     /**
7211      * Returns the index for a specified column id.
7212      * @param {String} id The column id
7213      * @return {Number} the index, or -1 if not found
7214      */
7215     getIndexById : function(id){
7216         for(var i = 0, len = this.config.length; i < len; i++){
7217             if(this.config[i].id == id){
7218                 return i;
7219             }
7220         }
7221         return -1;
7222     },
7223     
7224     /**
7225      * Returns the index for a specified column dataIndex.
7226      * @param {String} dataIndex The column dataIndex
7227      * @return {Number} the index, or -1 if not found
7228      */
7229     
7230     findColumnIndex : function(dataIndex){
7231         for(var i = 0, len = this.config.length; i < len; i++){
7232             if(this.config[i].dataIndex == dataIndex){
7233                 return i;
7234             }
7235         }
7236         return -1;
7237     },
7238     
7239     
7240     moveColumn : function(oldIndex, newIndex){
7241         var c = this.config[oldIndex];
7242         this.config.splice(oldIndex, 1);
7243         this.config.splice(newIndex, 0, c);
7244         this.dataMap = null;
7245         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7246     },
7247
7248     isLocked : function(colIndex){
7249         return this.config[colIndex].locked === true;
7250     },
7251
7252     setLocked : function(colIndex, value, suppressEvent){
7253         if(this.isLocked(colIndex) == value){
7254             return;
7255         }
7256         this.config[colIndex].locked = value;
7257         if(!suppressEvent){
7258             this.fireEvent("columnlockchange", this, colIndex, value);
7259         }
7260     },
7261
7262     getTotalLockedWidth : function(){
7263         var totalWidth = 0;
7264         for(var i = 0; i < this.config.length; i++){
7265             if(this.isLocked(i) && !this.isHidden(i)){
7266                 this.totalWidth += this.getColumnWidth(i);
7267             }
7268         }
7269         return totalWidth;
7270     },
7271
7272     getLockedCount : function(){
7273         for(var i = 0, len = this.config.length; i < len; i++){
7274             if(!this.isLocked(i)){
7275                 return i;
7276             }
7277         }
7278         
7279         return this.config.length;
7280     },
7281
7282     /**
7283      * Returns the number of columns.
7284      * @return {Number}
7285      */
7286     getColumnCount : function(visibleOnly){
7287         if(visibleOnly === true){
7288             var c = 0;
7289             for(var i = 0, len = this.config.length; i < len; i++){
7290                 if(!this.isHidden(i)){
7291                     c++;
7292                 }
7293             }
7294             return c;
7295         }
7296         return this.config.length;
7297     },
7298
7299     /**
7300      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7301      * @param {Function} fn
7302      * @param {Object} scope (optional)
7303      * @return {Array} result
7304      */
7305     getColumnsBy : function(fn, scope){
7306         var r = [];
7307         for(var i = 0, len = this.config.length; i < len; i++){
7308             var c = this.config[i];
7309             if(fn.call(scope||this, c, i) === true){
7310                 r[r.length] = c;
7311             }
7312         }
7313         return r;
7314     },
7315
7316     /**
7317      * Returns true if the specified column is sortable.
7318      * @param {Number} col The column index
7319      * @return {Boolean}
7320      */
7321     isSortable : function(col){
7322         if(typeof this.config[col].sortable == "undefined"){
7323             return this.defaultSortable;
7324         }
7325         return this.config[col].sortable;
7326     },
7327
7328     /**
7329      * Returns the rendering (formatting) function defined for the column.
7330      * @param {Number} col The column index.
7331      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7332      */
7333     getRenderer : function(col){
7334         if(!this.config[col].renderer){
7335             return Roo.grid.ColumnModel.defaultRenderer;
7336         }
7337         return this.config[col].renderer;
7338     },
7339
7340     /**
7341      * Sets the rendering (formatting) function for a column.
7342      * @param {Number} col The column index
7343      * @param {Function} fn The function to use to process the cell's raw data
7344      * to return HTML markup for the grid view. The render function is called with
7345      * the following parameters:<ul>
7346      * <li>Data value.</li>
7347      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7348      * <li>css A CSS style string to apply to the table cell.</li>
7349      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7350      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7351      * <li>Row index</li>
7352      * <li>Column index</li>
7353      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7354      */
7355     setRenderer : function(col, fn){
7356         this.config[col].renderer = fn;
7357     },
7358
7359     /**
7360      * Returns the width for the specified column.
7361      * @param {Number} col The column index
7362      * @return {Number}
7363      */
7364     getColumnWidth : function(col){
7365         return this.config[col].width * 1 || this.defaultWidth;
7366     },
7367
7368     /**
7369      * Sets the width for a column.
7370      * @param {Number} col The column index
7371      * @param {Number} width The new width
7372      */
7373     setColumnWidth : function(col, width, suppressEvent){
7374         this.config[col].width = width;
7375         this.totalWidth = null;
7376         if(!suppressEvent){
7377              this.fireEvent("widthchange", this, col, width);
7378         }
7379     },
7380
7381     /**
7382      * Returns the total width of all columns.
7383      * @param {Boolean} includeHidden True to include hidden column widths
7384      * @return {Number}
7385      */
7386     getTotalWidth : function(includeHidden){
7387         if(!this.totalWidth){
7388             this.totalWidth = 0;
7389             for(var i = 0, len = this.config.length; i < len; i++){
7390                 if(includeHidden || !this.isHidden(i)){
7391                     this.totalWidth += this.getColumnWidth(i);
7392                 }
7393             }
7394         }
7395         return this.totalWidth;
7396     },
7397
7398     /**
7399      * Returns the header for the specified column.
7400      * @param {Number} col The column index
7401      * @return {String}
7402      */
7403     getColumnHeader : function(col){
7404         return this.config[col].header;
7405     },
7406
7407     /**
7408      * Sets the header for a column.
7409      * @param {Number} col The column index
7410      * @param {String} header The new header
7411      */
7412     setColumnHeader : function(col, header){
7413         this.config[col].header = header;
7414         this.fireEvent("headerchange", this, col, header);
7415     },
7416
7417     /**
7418      * Returns the tooltip for the specified column.
7419      * @param {Number} col The column index
7420      * @return {String}
7421      */
7422     getColumnTooltip : function(col){
7423             return this.config[col].tooltip;
7424     },
7425     /**
7426      * Sets the tooltip for a column.
7427      * @param {Number} col The column index
7428      * @param {String} tooltip The new tooltip
7429      */
7430     setColumnTooltip : function(col, tooltip){
7431             this.config[col].tooltip = tooltip;
7432     },
7433
7434     /**
7435      * Returns the dataIndex for the specified column.
7436      * @param {Number} col The column index
7437      * @return {Number}
7438      */
7439     getDataIndex : function(col){
7440         return this.config[col].dataIndex;
7441     },
7442
7443     /**
7444      * Sets the dataIndex for a column.
7445      * @param {Number} col The column index
7446      * @param {Number} dataIndex The new dataIndex
7447      */
7448     setDataIndex : function(col, dataIndex){
7449         this.config[col].dataIndex = dataIndex;
7450     },
7451
7452     
7453     
7454     /**
7455      * Returns true if the cell is editable.
7456      * @param {Number} colIndex The column index
7457      * @param {Number} rowIndex The row index - this is nto actually used..?
7458      * @return {Boolean}
7459      */
7460     isCellEditable : function(colIndex, rowIndex){
7461         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7462     },
7463
7464     /**
7465      * Returns the editor defined for the cell/column.
7466      * return false or null to disable editing.
7467      * @param {Number} colIndex The column index
7468      * @param {Number} rowIndex The row index
7469      * @return {Object}
7470      */
7471     getCellEditor : function(colIndex, rowIndex){
7472         return this.config[colIndex].editor;
7473     },
7474
7475     /**
7476      * Sets if a column is editable.
7477      * @param {Number} col The column index
7478      * @param {Boolean} editable True if the column is editable
7479      */
7480     setEditable : function(col, editable){
7481         this.config[col].editable = editable;
7482     },
7483
7484
7485     /**
7486      * Returns true if the column is hidden.
7487      * @param {Number} colIndex The column index
7488      * @return {Boolean}
7489      */
7490     isHidden : function(colIndex){
7491         return this.config[colIndex].hidden;
7492     },
7493
7494
7495     /**
7496      * Returns true if the column width cannot be changed
7497      */
7498     isFixed : function(colIndex){
7499         return this.config[colIndex].fixed;
7500     },
7501
7502     /**
7503      * Returns true if the column can be resized
7504      * @return {Boolean}
7505      */
7506     isResizable : function(colIndex){
7507         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7508     },
7509     /**
7510      * Sets if a column is hidden.
7511      * @param {Number} colIndex The column index
7512      * @param {Boolean} hidden True if the column is hidden
7513      */
7514     setHidden : function(colIndex, hidden){
7515         this.config[colIndex].hidden = hidden;
7516         this.totalWidth = null;
7517         this.fireEvent("hiddenchange", this, colIndex, hidden);
7518     },
7519
7520     /**
7521      * Sets the editor for a column.
7522      * @param {Number} col The column index
7523      * @param {Object} editor The editor object
7524      */
7525     setEditor : function(col, editor){
7526         this.config[col].editor = editor;
7527     }
7528 });
7529
7530 Roo.grid.ColumnModel.defaultRenderer = function(value)
7531 {
7532     if(typeof value == "object") {
7533         return value;
7534     }
7535         if(typeof value == "string" && value.length < 1){
7536             return "&#160;";
7537         }
7538     
7539         return String.format("{0}", value);
7540 };
7541
7542 // Alias for backwards compatibility
7543 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7544 /*
7545  * Based on:
7546  * Ext JS Library 1.1.1
7547  * Copyright(c) 2006-2007, Ext JS, LLC.
7548  *
7549  * Originally Released Under LGPL - original licence link has changed is not relivant.
7550  *
7551  * Fork - LGPL
7552  * <script type="text/javascript">
7553  */
7554  
7555 /**
7556  * @class Roo.LoadMask
7557  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7558  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7559  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7560  * element's UpdateManager load indicator and will be destroyed after the initial load.
7561  * @constructor
7562  * Create a new LoadMask
7563  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7564  * @param {Object} config The config object
7565  */
7566 Roo.LoadMask = function(el, config){
7567     this.el = Roo.get(el);
7568     Roo.apply(this, config);
7569     if(this.store){
7570         this.store.on('beforeload', this.onBeforeLoad, this);
7571         this.store.on('load', this.onLoad, this);
7572         this.store.on('loadexception', this.onLoadException, this);
7573         this.removeMask = false;
7574     }else{
7575         var um = this.el.getUpdateManager();
7576         um.showLoadIndicator = false; // disable the default indicator
7577         um.on('beforeupdate', this.onBeforeLoad, this);
7578         um.on('update', this.onLoad, this);
7579         um.on('failure', this.onLoad, this);
7580         this.removeMask = true;
7581     }
7582 };
7583
7584 Roo.LoadMask.prototype = {
7585     /**
7586      * @cfg {Boolean} removeMask
7587      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7588      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7589      */
7590     /**
7591      * @cfg {String} msg
7592      * The text to display in a centered loading message box (defaults to 'Loading...')
7593      */
7594     msg : 'Loading...',
7595     /**
7596      * @cfg {String} msgCls
7597      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7598      */
7599     msgCls : 'x-mask-loading',
7600
7601     /**
7602      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7603      * @type Boolean
7604      */
7605     disabled: false,
7606
7607     /**
7608      * Disables the mask to prevent it from being displayed
7609      */
7610     disable : function(){
7611        this.disabled = true;
7612     },
7613
7614     /**
7615      * Enables the mask so that it can be displayed
7616      */
7617     enable : function(){
7618         this.disabled = false;
7619     },
7620     
7621     onLoadException : function()
7622     {
7623         Roo.log(arguments);
7624         
7625         if (typeof(arguments[3]) != 'undefined') {
7626             Roo.MessageBox.alert("Error loading",arguments[3]);
7627         } 
7628         /*
7629         try {
7630             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7631                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7632             }   
7633         } catch(e) {
7634             
7635         }
7636         */
7637     
7638         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7639     },
7640     // private
7641     onLoad : function()
7642     {
7643         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7644     },
7645
7646     // private
7647     onBeforeLoad : function(){
7648         if(!this.disabled){
7649             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7650         }
7651     },
7652
7653     // private
7654     destroy : function(){
7655         if(this.store){
7656             this.store.un('beforeload', this.onBeforeLoad, this);
7657             this.store.un('load', this.onLoad, this);
7658             this.store.un('loadexception', this.onLoadException, this);
7659         }else{
7660             var um = this.el.getUpdateManager();
7661             um.un('beforeupdate', this.onBeforeLoad, this);
7662             um.un('update', this.onLoad, this);
7663             um.un('failure', this.onLoad, this);
7664         }
7665     }
7666 };/*
7667  * - LGPL
7668  *
7669  * table
7670  * 
7671  */
7672
7673 /**
7674  * @class Roo.bootstrap.Table
7675  * @extends Roo.bootstrap.Component
7676  * Bootstrap Table class
7677  * @cfg {String} cls table class
7678  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7679  * @cfg {String} bgcolor Specifies the background color for a table
7680  * @cfg {Number} border Specifies whether the table cells should have borders or not
7681  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7682  * @cfg {Number} cellspacing Specifies the space between cells
7683  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7684  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7685  * @cfg {String} sortable Specifies that the table should be sortable
7686  * @cfg {String} summary Specifies a summary of the content of a table
7687  * @cfg {Number} width Specifies the width of a table
7688  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7689  * 
7690  * @cfg {boolean} striped Should the rows be alternative striped
7691  * @cfg {boolean} bordered Add borders to the table
7692  * @cfg {boolean} hover Add hover highlighting
7693  * @cfg {boolean} condensed Format condensed
7694  * @cfg {boolean} responsive Format condensed
7695  * @cfg {Boolean} loadMask (true|false) default false
7696  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7697  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7698  * @cfg {Boolean} rowSelection (true|false) default false
7699  * @cfg {Boolean} cellSelection (true|false) default false
7700  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7701  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7702  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7703  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7704  
7705  * 
7706  * @constructor
7707  * Create a new Table
7708  * @param {Object} config The config object
7709  */
7710
7711 Roo.bootstrap.Table = function(config){
7712     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7713     
7714   
7715     
7716     // BC...
7717     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7718     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7719     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7720     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7721     
7722     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7723     if (this.sm) {
7724         this.sm.grid = this;
7725         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7726         this.sm = this.selModel;
7727         this.sm.xmodule = this.xmodule || false;
7728     }
7729     
7730     if (this.cm && typeof(this.cm.config) == 'undefined') {
7731         this.colModel = new Roo.grid.ColumnModel(this.cm);
7732         this.cm = this.colModel;
7733         this.cm.xmodule = this.xmodule || false;
7734     }
7735     if (this.store) {
7736         this.store= Roo.factory(this.store, Roo.data);
7737         this.ds = this.store;
7738         this.ds.xmodule = this.xmodule || false;
7739          
7740     }
7741     if (this.footer && this.store) {
7742         this.footer.dataSource = this.ds;
7743         this.footer = Roo.factory(this.footer);
7744     }
7745     
7746     /** @private */
7747     this.addEvents({
7748         /**
7749          * @event cellclick
7750          * Fires when a cell is clicked
7751          * @param {Roo.bootstrap.Table} this
7752          * @param {Roo.Element} el
7753          * @param {Number} rowIndex
7754          * @param {Number} columnIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "cellclick" : true,
7758         /**
7759          * @event celldblclick
7760          * Fires when a cell is double clicked
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "celldblclick" : true,
7768         /**
7769          * @event rowclick
7770          * Fires when a row is clicked
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Roo.EventObject} e
7775          */
7776         "rowclick" : true,
7777         /**
7778          * @event rowdblclick
7779          * Fires when a row is double clicked
7780          * @param {Roo.bootstrap.Table} this
7781          * @param {Roo.Element} el
7782          * @param {Number} rowIndex
7783          * @param {Roo.EventObject} e
7784          */
7785         "rowdblclick" : true,
7786         /**
7787          * @event mouseover
7788          * Fires when a mouseover occur
7789          * @param {Roo.bootstrap.Table} this
7790          * @param {Roo.Element} el
7791          * @param {Number} rowIndex
7792          * @param {Number} columnIndex
7793          * @param {Roo.EventObject} e
7794          */
7795         "mouseover" : true,
7796         /**
7797          * @event mouseout
7798          * Fires when a mouseout occur
7799          * @param {Roo.bootstrap.Table} this
7800          * @param {Roo.Element} el
7801          * @param {Number} rowIndex
7802          * @param {Number} columnIndex
7803          * @param {Roo.EventObject} e
7804          */
7805         "mouseout" : true,
7806         /**
7807          * @event rowclass
7808          * Fires when a row is rendered, so you can change add a style to it.
7809          * @param {Roo.bootstrap.Table} this
7810          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7811          */
7812         'rowclass' : true,
7813           /**
7814          * @event rowsrendered
7815          * Fires when all the  rows have been rendered
7816          * @param {Roo.bootstrap.Table} this
7817          */
7818         'rowsrendered' : true,
7819         /**
7820          * @event contextmenu
7821          * The raw contextmenu event for the entire grid.
7822          * @param {Roo.EventObject} e
7823          */
7824         "contextmenu" : true,
7825         /**
7826          * @event rowcontextmenu
7827          * Fires when a row is right clicked
7828          * @param {Roo.bootstrap.Table} this
7829          * @param {Number} rowIndex
7830          * @param {Roo.EventObject} e
7831          */
7832         "rowcontextmenu" : true,
7833         /**
7834          * @event cellcontextmenu
7835          * Fires when a cell is right clicked
7836          * @param {Roo.bootstrap.Table} this
7837          * @param {Number} rowIndex
7838          * @param {Number} cellIndex
7839          * @param {Roo.EventObject} e
7840          */
7841          "cellcontextmenu" : true,
7842          /**
7843          * @event headercontextmenu
7844          * Fires when a header is right clicked
7845          * @param {Roo.bootstrap.Table} this
7846          * @param {Number} columnIndex
7847          * @param {Roo.EventObject} e
7848          */
7849         "headercontextmenu" : true
7850     });
7851 };
7852
7853 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7854     
7855     cls: false,
7856     align: false,
7857     bgcolor: false,
7858     border: false,
7859     cellpadding: false,
7860     cellspacing: false,
7861     frame: false,
7862     rules: false,
7863     sortable: false,
7864     summary: false,
7865     width: false,
7866     striped : false,
7867     scrollBody : false,
7868     bordered: false,
7869     hover:  false,
7870     condensed : false,
7871     responsive : false,
7872     sm : false,
7873     cm : false,
7874     store : false,
7875     loadMask : false,
7876     footerShow : true,
7877     headerShow : true,
7878   
7879     rowSelection : false,
7880     cellSelection : false,
7881     layout : false,
7882     
7883     // Roo.Element - the tbody
7884     mainBody: false,
7885     // Roo.Element - thead element
7886     mainHead: false,
7887     
7888     container: false, // used by gridpanel...
7889     
7890     lazyLoad : false,
7891     
7892     CSS : Roo.util.CSS,
7893     
7894     auto_hide_footer : false,
7895     
7896     getAutoCreate : function()
7897     {
7898         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7899         
7900         cfg = {
7901             tag: 'table',
7902             cls : 'table',
7903             cn : []
7904         };
7905         if (this.scrollBody) {
7906             cfg.cls += ' table-body-fixed';
7907         }    
7908         if (this.striped) {
7909             cfg.cls += ' table-striped';
7910         }
7911         
7912         if (this.hover) {
7913             cfg.cls += ' table-hover';
7914         }
7915         if (this.bordered) {
7916             cfg.cls += ' table-bordered';
7917         }
7918         if (this.condensed) {
7919             cfg.cls += ' table-condensed';
7920         }
7921         if (this.responsive) {
7922             cfg.cls += ' table-responsive';
7923         }
7924         
7925         if (this.cls) {
7926             cfg.cls+=  ' ' +this.cls;
7927         }
7928         
7929         // this lot should be simplifed...
7930         var _t = this;
7931         var cp = [
7932             'align',
7933             'bgcolor',
7934             'border',
7935             'cellpadding',
7936             'cellspacing',
7937             'frame',
7938             'rules',
7939             'sortable',
7940             'summary',
7941             'width'
7942         ].forEach(function(k) {
7943             if (_t[k]) {
7944                 cfg[k] = _t[k];
7945             }
7946         });
7947         
7948         
7949         if (this.layout) {
7950             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7951         }
7952         
7953         if(this.store || this.cm){
7954             if(this.headerShow){
7955                 cfg.cn.push(this.renderHeader());
7956             }
7957             
7958             cfg.cn.push(this.renderBody());
7959             
7960             if(this.footerShow){
7961                 cfg.cn.push(this.renderFooter());
7962             }
7963             // where does this come from?
7964             //cfg.cls+=  ' TableGrid';
7965         }
7966         
7967         return { cn : [ cfg ] };
7968     },
7969     
7970     initEvents : function()
7971     {   
7972         if(!this.store || !this.cm){
7973             return;
7974         }
7975         if (this.selModel) {
7976             this.selModel.initEvents();
7977         }
7978         
7979         
7980         //Roo.log('initEvents with ds!!!!');
7981         
7982         this.mainBody = this.el.select('tbody', true).first();
7983         this.mainHead = this.el.select('thead', true).first();
7984         this.mainFoot = this.el.select('tfoot', true).first();
7985         
7986         
7987         
7988         var _this = this;
7989         
7990         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7991             e.on('click', _this.sort, _this);
7992         });
7993         
7994         this.mainBody.on("click", this.onClick, this);
7995         this.mainBody.on("dblclick", this.onDblClick, this);
7996         
7997         // why is this done????? = it breaks dialogs??
7998         //this.parent().el.setStyle('position', 'relative');
7999         
8000         
8001         if (this.footer) {
8002             this.footer.parentId = this.id;
8003             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8004             
8005             if(this.lazyLoad){
8006                 this.el.select('tfoot tr td').first().addClass('hide');
8007             }
8008         } 
8009         
8010         if(this.loadMask) {
8011             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8012         }
8013         
8014         this.store.on('load', this.onLoad, this);
8015         this.store.on('beforeload', this.onBeforeLoad, this);
8016         this.store.on('update', this.onUpdate, this);
8017         this.store.on('add', this.onAdd, this);
8018         this.store.on("clear", this.clear, this);
8019         
8020         this.el.on("contextmenu", this.onContextMenu, this);
8021         
8022         this.mainBody.on('scroll', this.onBodyScroll, this);
8023         
8024         this.cm.on("headerchange", this.onHeaderChange, this);
8025         
8026         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8027         
8028     },
8029     
8030     onContextMenu : function(e, t)
8031     {
8032         this.processEvent("contextmenu", e);
8033     },
8034     
8035     processEvent : function(name, e)
8036     {
8037         if (name != 'touchstart' ) {
8038             this.fireEvent(name, e);    
8039         }
8040         
8041         var t = e.getTarget();
8042         
8043         var cell = Roo.get(t);
8044         
8045         if(!cell){
8046             return;
8047         }
8048         
8049         if(cell.findParent('tfoot', false, true)){
8050             return;
8051         }
8052         
8053         if(cell.findParent('thead', false, true)){
8054             
8055             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8056                 cell = Roo.get(t).findParent('th', false, true);
8057                 if (!cell) {
8058                     Roo.log("failed to find th in thead?");
8059                     Roo.log(e.getTarget());
8060                     return;
8061                 }
8062             }
8063             
8064             var cellIndex = cell.dom.cellIndex;
8065             
8066             var ename = name == 'touchstart' ? 'click' : name;
8067             this.fireEvent("header" + ename, this, cellIndex, e);
8068             
8069             return;
8070         }
8071         
8072         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8073             cell = Roo.get(t).findParent('td', false, true);
8074             if (!cell) {
8075                 Roo.log("failed to find th in tbody?");
8076                 Roo.log(e.getTarget());
8077                 return;
8078             }
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1;
8084         
8085         if(row !== false){
8086             
8087             this.fireEvent("row" + name, this, rowIndex, e);
8088             
8089             if(cell !== false){
8090             
8091                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8092             }
8093         }
8094         
8095     },
8096     
8097     onMouseover : function(e, el)
8098     {
8099         var cell = Roo.get(el);
8100         
8101         if(!cell){
8102             return;
8103         }
8104         
8105         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106             cell = cell.findParent('td', false, true);
8107         }
8108         
8109         var row = cell.findParent('tr', false, true);
8110         var cellIndex = cell.dom.cellIndex;
8111         var rowIndex = row.dom.rowIndex - 1; // start from 0
8112         
8113         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8114         
8115     },
8116     
8117     onMouseout : function(e, el)
8118     {
8119         var cell = Roo.get(el);
8120         
8121         if(!cell){
8122             return;
8123         }
8124         
8125         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126             cell = cell.findParent('td', false, true);
8127         }
8128         
8129         var row = cell.findParent('tr', false, true);
8130         var cellIndex = cell.dom.cellIndex;
8131         var rowIndex = row.dom.rowIndex - 1; // start from 0
8132         
8133         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8134         
8135     },
8136     
8137     onClick : function(e, el)
8138     {
8139         var cell = Roo.get(el);
8140         
8141         if(!cell || (!this.cellSelection && !this.rowSelection)){
8142             return;
8143         }
8144         
8145         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8146             cell = cell.findParent('td', false, true);
8147         }
8148         
8149         if(!cell || typeof(cell) == 'undefined'){
8150             return;
8151         }
8152         
8153         var row = cell.findParent('tr', false, true);
8154         
8155         if(!row || typeof(row) == 'undefined'){
8156             return;
8157         }
8158         
8159         var cellIndex = cell.dom.cellIndex;
8160         var rowIndex = this.getRowIndex(row);
8161         
8162         // why??? - should these not be based on SelectionModel?
8163         if(this.cellSelection){
8164             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8165         }
8166         
8167         if(this.rowSelection){
8168             this.fireEvent('rowclick', this, row, rowIndex, e);
8169         }
8170         
8171         
8172     },
8173         
8174     onDblClick : function(e,el)
8175     {
8176         var cell = Roo.get(el);
8177         
8178         if(!cell || (!this.cellSelection && !this.rowSelection)){
8179             return;
8180         }
8181         
8182         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8183             cell = cell.findParent('td', false, true);
8184         }
8185         
8186         if(!cell || typeof(cell) == 'undefined'){
8187             return;
8188         }
8189         
8190         var row = cell.findParent('tr', false, true);
8191         
8192         if(!row || typeof(row) == 'undefined'){
8193             return;
8194         }
8195         
8196         var cellIndex = cell.dom.cellIndex;
8197         var rowIndex = this.getRowIndex(row);
8198         
8199         if(this.cellSelection){
8200             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8201         }
8202         
8203         if(this.rowSelection){
8204             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8205         }
8206     },
8207     
8208     sort : function(e,el)
8209     {
8210         var col = Roo.get(el);
8211         
8212         if(!col.hasClass('sortable')){
8213             return;
8214         }
8215         
8216         var sort = col.attr('sort');
8217         var dir = 'ASC';
8218         
8219         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8220             dir = 'DESC';
8221         }
8222         
8223         this.store.sortInfo = {field : sort, direction : dir};
8224         
8225         if (this.footer) {
8226             Roo.log("calling footer first");
8227             this.footer.onClick('first');
8228         } else {
8229         
8230             this.store.load({ params : { start : 0 } });
8231         }
8232     },
8233     
8234     renderHeader : function()
8235     {
8236         var header = {
8237             tag: 'thead',
8238             cn : []
8239         };
8240         
8241         var cm = this.cm;
8242         this.totalWidth = 0;
8243         
8244         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8245             
8246             var config = cm.config[i];
8247             
8248             var c = {
8249                 tag: 'th',
8250                 cls : 'x-hcol-' + i,
8251                 style : '',
8252                 html: cm.getColumnHeader(i)
8253             };
8254             
8255             var hh = '';
8256             
8257             if(typeof(config.sortable) != 'undefined' && config.sortable){
8258                 c.cls = 'sortable';
8259                 c.html = '<i class="glyphicon"></i>' + c.html;
8260             }
8261             
8262             // could use BS4 hidden-..-down 
8263             
8264             if(typeof(config.lgHeader) != 'undefined'){
8265                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8266             }
8267             
8268             if(typeof(config.mdHeader) != 'undefined'){
8269                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8270             }
8271             
8272             if(typeof(config.smHeader) != 'undefined'){
8273                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8274             }
8275             
8276             if(typeof(config.xsHeader) != 'undefined'){
8277                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8278             }
8279             
8280             if(hh.length){
8281                 c.html = hh;
8282             }
8283             
8284             if(typeof(config.tooltip) != 'undefined'){
8285                 c.tooltip = config.tooltip;
8286             }
8287             
8288             if(typeof(config.colspan) != 'undefined'){
8289                 c.colspan = config.colspan;
8290             }
8291             
8292             if(typeof(config.hidden) != 'undefined' && config.hidden){
8293                 c.style += ' display:none;';
8294             }
8295             
8296             if(typeof(config.dataIndex) != 'undefined'){
8297                 c.sort = config.dataIndex;
8298             }
8299             
8300            
8301             
8302             if(typeof(config.align) != 'undefined' && config.align.length){
8303                 c.style += ' text-align:' + config.align + ';';
8304             }
8305             
8306             if(typeof(config.width) != 'undefined'){
8307                 c.style += ' width:' + config.width + 'px;';
8308                 this.totalWidth += config.width;
8309             } else {
8310                 this.totalWidth += 100; // assume minimum of 100 per column?
8311             }
8312             
8313             if(typeof(config.cls) != 'undefined'){
8314                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8315             }
8316             
8317             ['xs','sm','md','lg'].map(function(size){
8318                 
8319                 if(typeof(config[size]) == 'undefined'){
8320                     return;
8321                 }
8322                  
8323                 if (!config[size]) { // 0 = hidden
8324                     // BS 4 '0' is treated as hide that column and below.
8325                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8326                     return;
8327                 }
8328                 
8329                 c.cls += ' col-' + size + '-' + config[size] + (
8330                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8331                 );
8332                 
8333                 
8334             });
8335             
8336             header.cn.push(c)
8337         }
8338         
8339         return header;
8340     },
8341     
8342     renderBody : function()
8343     {
8344         var body = {
8345             tag: 'tbody',
8346             cn : [
8347                 {
8348                     tag: 'tr',
8349                     cn : [
8350                         {
8351                             tag : 'td',
8352                             colspan :  this.cm.getColumnCount()
8353                         }
8354                     ]
8355                 }
8356             ]
8357         };
8358         
8359         return body;
8360     },
8361     
8362     renderFooter : function()
8363     {
8364         var footer = {
8365             tag: 'tfoot',
8366             cn : [
8367                 {
8368                     tag: 'tr',
8369                     cn : [
8370                         {
8371                             tag : 'td',
8372                             colspan :  this.cm.getColumnCount()
8373                         }
8374                     ]
8375                 }
8376             ]
8377         };
8378         
8379         return footer;
8380     },
8381     
8382     
8383     
8384     onLoad : function()
8385     {
8386 //        Roo.log('ds onload');
8387         this.clear();
8388         
8389         var _this = this;
8390         var cm = this.cm;
8391         var ds = this.store;
8392         
8393         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8394             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8395             if (_this.store.sortInfo) {
8396                     
8397                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8398                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8399                 }
8400                 
8401                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8402                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8403                 }
8404             }
8405         });
8406         
8407         var tbody =  this.mainBody;
8408               
8409         if(ds.getCount() > 0){
8410             ds.data.each(function(d,rowIndex){
8411                 var row =  this.renderRow(cm, ds, rowIndex);
8412                 
8413                 tbody.createChild(row);
8414                 
8415                 var _this = this;
8416                 
8417                 if(row.cellObjects.length){
8418                     Roo.each(row.cellObjects, function(r){
8419                         _this.renderCellObject(r);
8420                     })
8421                 }
8422                 
8423             }, this);
8424         }
8425         
8426         var tfoot = this.el.select('tfoot', true).first();
8427         
8428         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8429             
8430             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8431             
8432             var total = this.ds.getTotalCount();
8433             
8434             if(this.footer.pageSize < total){
8435                 this.mainFoot.show();
8436             }
8437         }
8438         
8439         Roo.each(this.el.select('tbody td', true).elements, function(e){
8440             e.on('mouseover', _this.onMouseover, _this);
8441         });
8442         
8443         Roo.each(this.el.select('tbody td', true).elements, function(e){
8444             e.on('mouseout', _this.onMouseout, _this);
8445         });
8446         this.fireEvent('rowsrendered', this);
8447         
8448         this.autoSize();
8449     },
8450     
8451     
8452     onUpdate : function(ds,record)
8453     {
8454         this.refreshRow(record);
8455         this.autoSize();
8456     },
8457     
8458     onRemove : function(ds, record, index, isUpdate){
8459         if(isUpdate !== true){
8460             this.fireEvent("beforerowremoved", this, index, record);
8461         }
8462         var bt = this.mainBody.dom;
8463         
8464         var rows = this.el.select('tbody > tr', true).elements;
8465         
8466         if(typeof(rows[index]) != 'undefined'){
8467             bt.removeChild(rows[index].dom);
8468         }
8469         
8470 //        if(bt.rows[index]){
8471 //            bt.removeChild(bt.rows[index]);
8472 //        }
8473         
8474         if(isUpdate !== true){
8475             //this.stripeRows(index);
8476             //this.syncRowHeights(index, index);
8477             //this.layout();
8478             this.fireEvent("rowremoved", this, index, record);
8479         }
8480     },
8481     
8482     onAdd : function(ds, records, rowIndex)
8483     {
8484         //Roo.log('on Add called');
8485         // - note this does not handle multiple adding very well..
8486         var bt = this.mainBody.dom;
8487         for (var i =0 ; i < records.length;i++) {
8488             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8489             //Roo.log(records[i]);
8490             //Roo.log(this.store.getAt(rowIndex+i));
8491             this.insertRow(this.store, rowIndex + i, false);
8492             return;
8493         }
8494         
8495     },
8496     
8497     
8498     refreshRow : function(record){
8499         var ds = this.store, index;
8500         if(typeof record == 'number'){
8501             index = record;
8502             record = ds.getAt(index);
8503         }else{
8504             index = ds.indexOf(record);
8505             if (index < 0) {
8506                 return; // should not happen - but seems to 
8507             }
8508         }
8509         this.insertRow(ds, index, true);
8510         this.autoSize();
8511         this.onRemove(ds, record, index+1, true);
8512         this.autoSize();
8513         //this.syncRowHeights(index, index);
8514         //this.layout();
8515         this.fireEvent("rowupdated", this, index, record);
8516     },
8517     
8518     insertRow : function(dm, rowIndex, isUpdate){
8519         
8520         if(!isUpdate){
8521             this.fireEvent("beforerowsinserted", this, rowIndex);
8522         }
8523             //var s = this.getScrollState();
8524         var row = this.renderRow(this.cm, this.store, rowIndex);
8525         // insert before rowIndex..
8526         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8527         
8528         var _this = this;
8529                 
8530         if(row.cellObjects.length){
8531             Roo.each(row.cellObjects, function(r){
8532                 _this.renderCellObject(r);
8533             })
8534         }
8535             
8536         if(!isUpdate){
8537             this.fireEvent("rowsinserted", this, rowIndex);
8538             //this.syncRowHeights(firstRow, lastRow);
8539             //this.stripeRows(firstRow);
8540             //this.layout();
8541         }
8542         
8543     },
8544     
8545     
8546     getRowDom : function(rowIndex)
8547     {
8548         var rows = this.el.select('tbody > tr', true).elements;
8549         
8550         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8551         
8552     },
8553     // returns the object tree for a tr..
8554   
8555     
8556     renderRow : function(cm, ds, rowIndex) 
8557     {
8558         var d = ds.getAt(rowIndex);
8559         
8560         var row = {
8561             tag : 'tr',
8562             cls : 'x-row-' + rowIndex,
8563             cn : []
8564         };
8565             
8566         var cellObjects = [];
8567         
8568         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8569             var config = cm.config[i];
8570             
8571             var renderer = cm.getRenderer(i);
8572             var value = '';
8573             var id = false;
8574             
8575             if(typeof(renderer) !== 'undefined'){
8576                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8577             }
8578             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8579             // and are rendered into the cells after the row is rendered - using the id for the element.
8580             
8581             if(typeof(value) === 'object'){
8582                 id = Roo.id();
8583                 cellObjects.push({
8584                     container : id,
8585                     cfg : value 
8586                 })
8587             }
8588             
8589             var rowcfg = {
8590                 record: d,
8591                 rowIndex : rowIndex,
8592                 colIndex : i,
8593                 rowClass : ''
8594             };
8595
8596             this.fireEvent('rowclass', this, rowcfg);
8597             
8598             var td = {
8599                 tag: 'td',
8600                 cls : rowcfg.rowClass + ' x-col-' + i,
8601                 style: '',
8602                 html: (typeof(value) === 'object') ? '' : value
8603             };
8604             
8605             if (id) {
8606                 td.id = id;
8607             }
8608             
8609             if(typeof(config.colspan) != 'undefined'){
8610                 td.colspan = config.colspan;
8611             }
8612             
8613             if(typeof(config.hidden) != 'undefined' && config.hidden){
8614                 td.style += ' display:none;';
8615             }
8616             
8617             if(typeof(config.align) != 'undefined' && config.align.length){
8618                 td.style += ' text-align:' + config.align + ';';
8619             }
8620             if(typeof(config.valign) != 'undefined' && config.valign.length){
8621                 td.style += ' vertical-align:' + config.valign + ';';
8622             }
8623             
8624             if(typeof(config.width) != 'undefined'){
8625                 td.style += ' width:' +  config.width + 'px;';
8626             }
8627             
8628             if(typeof(config.cursor) != 'undefined'){
8629                 td.style += ' cursor:' +  config.cursor + ';';
8630             }
8631             
8632             if(typeof(config.cls) != 'undefined'){
8633                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8634             }
8635             
8636             ['xs','sm','md','lg'].map(function(size){
8637                 
8638                 if(typeof(config[size]) == 'undefined'){
8639                     return;
8640                 }
8641                 
8642                 
8643                   
8644                 if (!config[size]) { // 0 = hidden
8645                     // BS 4 '0' is treated as hide that column and below.
8646                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8647                     return;
8648                 }
8649                 
8650                 td.cls += ' col-' + size + '-' + config[size] + (
8651                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8652                 );
8653                  
8654
8655             });
8656             
8657             row.cn.push(td);
8658            
8659         }
8660         
8661         row.cellObjects = cellObjects;
8662         
8663         return row;
8664           
8665     },
8666     
8667     
8668     
8669     onBeforeLoad : function()
8670     {
8671         
8672     },
8673      /**
8674      * Remove all rows
8675      */
8676     clear : function()
8677     {
8678         this.el.select('tbody', true).first().dom.innerHTML = '';
8679     },
8680     /**
8681      * Show or hide a row.
8682      * @param {Number} rowIndex to show or hide
8683      * @param {Boolean} state hide
8684      */
8685     setRowVisibility : function(rowIndex, state)
8686     {
8687         var bt = this.mainBody.dom;
8688         
8689         var rows = this.el.select('tbody > tr', true).elements;
8690         
8691         if(typeof(rows[rowIndex]) == 'undefined'){
8692             return;
8693         }
8694         rows[rowIndex].dom.style.display = state ? '' : 'none';
8695     },
8696     
8697     
8698     getSelectionModel : function(){
8699         if(!this.selModel){
8700             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8701         }
8702         return this.selModel;
8703     },
8704     /*
8705      * Render the Roo.bootstrap object from renderder
8706      */
8707     renderCellObject : function(r)
8708     {
8709         var _this = this;
8710         
8711         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8712         
8713         var t = r.cfg.render(r.container);
8714         
8715         if(r.cfg.cn){
8716             Roo.each(r.cfg.cn, function(c){
8717                 var child = {
8718                     container: t.getChildContainer(),
8719                     cfg: c
8720                 };
8721                 _this.renderCellObject(child);
8722             })
8723         }
8724     },
8725     
8726     getRowIndex : function(row)
8727     {
8728         var rowIndex = -1;
8729         
8730         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8731             if(el != row){
8732                 return;
8733             }
8734             
8735             rowIndex = index;
8736         });
8737         
8738         return rowIndex;
8739     },
8740      /**
8741      * Returns the grid's underlying element = used by panel.Grid
8742      * @return {Element} The element
8743      */
8744     getGridEl : function(){
8745         return this.el;
8746     },
8747      /**
8748      * Forces a resize - used by panel.Grid
8749      * @return {Element} The element
8750      */
8751     autoSize : function()
8752     {
8753         //var ctr = Roo.get(this.container.dom.parentElement);
8754         var ctr = Roo.get(this.el.dom);
8755         
8756         var thd = this.getGridEl().select('thead',true).first();
8757         var tbd = this.getGridEl().select('tbody', true).first();
8758         var tfd = this.getGridEl().select('tfoot', true).first();
8759         
8760         var cw = ctr.getWidth();
8761         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8762         
8763         if (tbd) {
8764             
8765             tbd.setWidth(ctr.getWidth());
8766             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8767             // this needs fixing for various usage - currently only hydra job advers I think..
8768             //tdb.setHeight(
8769             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8770             //); 
8771             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8772             cw -= barsize;
8773         }
8774         cw = Math.max(cw, this.totalWidth);
8775         this.getGridEl().select('tbody tr',true).setWidth(cw);
8776         
8777         // resize 'expandable coloumn?
8778         
8779         return; // we doe not have a view in this design..
8780         
8781     },
8782     onBodyScroll: function()
8783     {
8784         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8785         if(this.mainHead){
8786             this.mainHead.setStyle({
8787                 'position' : 'relative',
8788                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8789             });
8790         }
8791         
8792         if(this.lazyLoad){
8793             
8794             var scrollHeight = this.mainBody.dom.scrollHeight;
8795             
8796             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8797             
8798             var height = this.mainBody.getHeight();
8799             
8800             if(scrollHeight - height == scrollTop) {
8801                 
8802                 var total = this.ds.getTotalCount();
8803                 
8804                 if(this.footer.cursor + this.footer.pageSize < total){
8805                     
8806                     this.footer.ds.load({
8807                         params : {
8808                             start : this.footer.cursor + this.footer.pageSize,
8809                             limit : this.footer.pageSize
8810                         },
8811                         add : true
8812                     });
8813                 }
8814             }
8815             
8816         }
8817     },
8818     
8819     onHeaderChange : function()
8820     {
8821         var header = this.renderHeader();
8822         var table = this.el.select('table', true).first();
8823         
8824         this.mainHead.remove();
8825         this.mainHead = table.createChild(header, this.mainBody, false);
8826     },
8827     
8828     onHiddenChange : function(colModel, colIndex, hidden)
8829     {
8830         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8831         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8832         
8833         this.CSS.updateRule(thSelector, "display", "");
8834         this.CSS.updateRule(tdSelector, "display", "");
8835         
8836         if(hidden){
8837             this.CSS.updateRule(thSelector, "display", "none");
8838             this.CSS.updateRule(tdSelector, "display", "none");
8839         }
8840         
8841         this.onHeaderChange();
8842         this.onLoad();
8843     },
8844     
8845     setColumnWidth: function(col_index, width)
8846     {
8847         // width = "md-2 xs-2..."
8848         if(!this.colModel.config[col_index]) {
8849             return;
8850         }
8851         
8852         var w = width.split(" ");
8853         
8854         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8855         
8856         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8857         
8858         
8859         for(var j = 0; j < w.length; j++) {
8860             
8861             if(!w[j]) {
8862                 continue;
8863             }
8864             
8865             var size_cls = w[j].split("-");
8866             
8867             if(!Number.isInteger(size_cls[1] * 1)) {
8868                 continue;
8869             }
8870             
8871             if(!this.colModel.config[col_index][size_cls[0]]) {
8872                 continue;
8873             }
8874             
8875             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8876                 continue;
8877             }
8878             
8879             h_row[0].classList.replace(
8880                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8881                 "col-"+size_cls[0]+"-"+size_cls[1]
8882             );
8883             
8884             for(var i = 0; i < rows.length; i++) {
8885                 
8886                 var size_cls = w[j].split("-");
8887                 
8888                 if(!Number.isInteger(size_cls[1] * 1)) {
8889                     continue;
8890                 }
8891                 
8892                 if(!this.colModel.config[col_index][size_cls[0]]) {
8893                     continue;
8894                 }
8895                 
8896                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8897                     continue;
8898                 }
8899                 
8900                 rows[i].classList.replace(
8901                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8902                     "col-"+size_cls[0]+"-"+size_cls[1]
8903                 );
8904             }
8905             
8906             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8907         }
8908     }
8909 });
8910
8911  
8912
8913  /*
8914  * - LGPL
8915  *
8916  * table cell
8917  * 
8918  */
8919
8920 /**
8921  * @class Roo.bootstrap.TableCell
8922  * @extends Roo.bootstrap.Component
8923  * Bootstrap TableCell class
8924  * @cfg {String} html cell contain text
8925  * @cfg {String} cls cell class
8926  * @cfg {String} tag cell tag (td|th) default td
8927  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8928  * @cfg {String} align Aligns the content in a cell
8929  * @cfg {String} axis Categorizes cells
8930  * @cfg {String} bgcolor Specifies the background color of a cell
8931  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8932  * @cfg {Number} colspan Specifies the number of columns a cell should span
8933  * @cfg {String} headers Specifies one or more header cells a cell is related to
8934  * @cfg {Number} height Sets the height of a cell
8935  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8936  * @cfg {Number} rowspan Sets the number of rows a cell should span
8937  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8938  * @cfg {String} valign Vertical aligns the content in a cell
8939  * @cfg {Number} width Specifies the width of a cell
8940  * 
8941  * @constructor
8942  * Create a new TableCell
8943  * @param {Object} config The config object
8944  */
8945
8946 Roo.bootstrap.TableCell = function(config){
8947     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8948 };
8949
8950 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8951     
8952     html: false,
8953     cls: false,
8954     tag: false,
8955     abbr: false,
8956     align: false,
8957     axis: false,
8958     bgcolor: false,
8959     charoff: false,
8960     colspan: false,
8961     headers: false,
8962     height: false,
8963     nowrap: false,
8964     rowspan: false,
8965     scope: false,
8966     valign: false,
8967     width: false,
8968     
8969     
8970     getAutoCreate : function(){
8971         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8972         
8973         cfg = {
8974             tag: 'td'
8975         };
8976         
8977         if(this.tag){
8978             cfg.tag = this.tag;
8979         }
8980         
8981         if (this.html) {
8982             cfg.html=this.html
8983         }
8984         if (this.cls) {
8985             cfg.cls=this.cls
8986         }
8987         if (this.abbr) {
8988             cfg.abbr=this.abbr
8989         }
8990         if (this.align) {
8991             cfg.align=this.align
8992         }
8993         if (this.axis) {
8994             cfg.axis=this.axis
8995         }
8996         if (this.bgcolor) {
8997             cfg.bgcolor=this.bgcolor
8998         }
8999         if (this.charoff) {
9000             cfg.charoff=this.charoff
9001         }
9002         if (this.colspan) {
9003             cfg.colspan=this.colspan
9004         }
9005         if (this.headers) {
9006             cfg.headers=this.headers
9007         }
9008         if (this.height) {
9009             cfg.height=this.height
9010         }
9011         if (this.nowrap) {
9012             cfg.nowrap=this.nowrap
9013         }
9014         if (this.rowspan) {
9015             cfg.rowspan=this.rowspan
9016         }
9017         if (this.scope) {
9018             cfg.scope=this.scope
9019         }
9020         if (this.valign) {
9021             cfg.valign=this.valign
9022         }
9023         if (this.width) {
9024             cfg.width=this.width
9025         }
9026         
9027         
9028         return cfg;
9029     }
9030    
9031 });
9032
9033  
9034
9035  /*
9036  * - LGPL
9037  *
9038  * table row
9039  * 
9040  */
9041
9042 /**
9043  * @class Roo.bootstrap.TableRow
9044  * @extends Roo.bootstrap.Component
9045  * Bootstrap TableRow class
9046  * @cfg {String} cls row class
9047  * @cfg {String} align Aligns the content in a table row
9048  * @cfg {String} bgcolor Specifies a background color for a table row
9049  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9050  * @cfg {String} valign Vertical aligns the content in a table row
9051  * 
9052  * @constructor
9053  * Create a new TableRow
9054  * @param {Object} config The config object
9055  */
9056
9057 Roo.bootstrap.TableRow = function(config){
9058     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9059 };
9060
9061 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9062     
9063     cls: false,
9064     align: false,
9065     bgcolor: false,
9066     charoff: false,
9067     valign: false,
9068     
9069     getAutoCreate : function(){
9070         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9071         
9072         cfg = {
9073             tag: 'tr'
9074         };
9075             
9076         if(this.cls){
9077             cfg.cls = this.cls;
9078         }
9079         if(this.align){
9080             cfg.align = this.align;
9081         }
9082         if(this.bgcolor){
9083             cfg.bgcolor = this.bgcolor;
9084         }
9085         if(this.charoff){
9086             cfg.charoff = this.charoff;
9087         }
9088         if(this.valign){
9089             cfg.valign = this.valign;
9090         }
9091         
9092         return cfg;
9093     }
9094    
9095 });
9096
9097  
9098
9099  /*
9100  * - LGPL
9101  *
9102  * table body
9103  * 
9104  */
9105
9106 /**
9107  * @class Roo.bootstrap.TableBody
9108  * @extends Roo.bootstrap.Component
9109  * Bootstrap TableBody class
9110  * @cfg {String} cls element class
9111  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9112  * @cfg {String} align Aligns the content inside the element
9113  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9114  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9115  * 
9116  * @constructor
9117  * Create a new TableBody
9118  * @param {Object} config The config object
9119  */
9120
9121 Roo.bootstrap.TableBody = function(config){
9122     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9123 };
9124
9125 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9126     
9127     cls: false,
9128     tag: false,
9129     align: false,
9130     charoff: false,
9131     valign: false,
9132     
9133     getAutoCreate : function(){
9134         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9135         
9136         cfg = {
9137             tag: 'tbody'
9138         };
9139             
9140         if (this.cls) {
9141             cfg.cls=this.cls
9142         }
9143         if(this.tag){
9144             cfg.tag = this.tag;
9145         }
9146         
9147         if(this.align){
9148             cfg.align = this.align;
9149         }
9150         if(this.charoff){
9151             cfg.charoff = this.charoff;
9152         }
9153         if(this.valign){
9154             cfg.valign = this.valign;
9155         }
9156         
9157         return cfg;
9158     }
9159     
9160     
9161 //    initEvents : function()
9162 //    {
9163 //        
9164 //        if(!this.store){
9165 //            return;
9166 //        }
9167 //        
9168 //        this.store = Roo.factory(this.store, Roo.data);
9169 //        this.store.on('load', this.onLoad, this);
9170 //        
9171 //        this.store.load();
9172 //        
9173 //    },
9174 //    
9175 //    onLoad: function () 
9176 //    {   
9177 //        this.fireEvent('load', this);
9178 //    }
9179 //    
9180 //   
9181 });
9182
9183  
9184
9185  /*
9186  * Based on:
9187  * Ext JS Library 1.1.1
9188  * Copyright(c) 2006-2007, Ext JS, LLC.
9189  *
9190  * Originally Released Under LGPL - original licence link has changed is not relivant.
9191  *
9192  * Fork - LGPL
9193  * <script type="text/javascript">
9194  */
9195
9196 // as we use this in bootstrap.
9197 Roo.namespace('Roo.form');
9198  /**
9199  * @class Roo.form.Action
9200  * Internal Class used to handle form actions
9201  * @constructor
9202  * @param {Roo.form.BasicForm} el The form element or its id
9203  * @param {Object} config Configuration options
9204  */
9205
9206  
9207  
9208 // define the action interface
9209 Roo.form.Action = function(form, options){
9210     this.form = form;
9211     this.options = options || {};
9212 };
9213 /**
9214  * Client Validation Failed
9215  * @const 
9216  */
9217 Roo.form.Action.CLIENT_INVALID = 'client';
9218 /**
9219  * Server Validation Failed
9220  * @const 
9221  */
9222 Roo.form.Action.SERVER_INVALID = 'server';
9223  /**
9224  * Connect to Server Failed
9225  * @const 
9226  */
9227 Roo.form.Action.CONNECT_FAILURE = 'connect';
9228 /**
9229  * Reading Data from Server Failed
9230  * @const 
9231  */
9232 Roo.form.Action.LOAD_FAILURE = 'load';
9233
9234 Roo.form.Action.prototype = {
9235     type : 'default',
9236     failureType : undefined,
9237     response : undefined,
9238     result : undefined,
9239
9240     // interface method
9241     run : function(options){
9242
9243     },
9244
9245     // interface method
9246     success : function(response){
9247
9248     },
9249
9250     // interface method
9251     handleResponse : function(response){
9252
9253     },
9254
9255     // default connection failure
9256     failure : function(response){
9257         
9258         this.response = response;
9259         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9260         this.form.afterAction(this, false);
9261     },
9262
9263     processResponse : function(response){
9264         this.response = response;
9265         if(!response.responseText){
9266             return true;
9267         }
9268         this.result = this.handleResponse(response);
9269         return this.result;
9270     },
9271
9272     // utility functions used internally
9273     getUrl : function(appendParams){
9274         var url = this.options.url || this.form.url || this.form.el.dom.action;
9275         if(appendParams){
9276             var p = this.getParams();
9277             if(p){
9278                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9279             }
9280         }
9281         return url;
9282     },
9283
9284     getMethod : function(){
9285         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9286     },
9287
9288     getParams : function(){
9289         var bp = this.form.baseParams;
9290         var p = this.options.params;
9291         if(p){
9292             if(typeof p == "object"){
9293                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9294             }else if(typeof p == 'string' && bp){
9295                 p += '&' + Roo.urlEncode(bp);
9296             }
9297         }else if(bp){
9298             p = Roo.urlEncode(bp);
9299         }
9300         return p;
9301     },
9302
9303     createCallback : function(){
9304         return {
9305             success: this.success,
9306             failure: this.failure,
9307             scope: this,
9308             timeout: (this.form.timeout*1000),
9309             upload: this.form.fileUpload ? this.success : undefined
9310         };
9311     }
9312 };
9313
9314 Roo.form.Action.Submit = function(form, options){
9315     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9316 };
9317
9318 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9319     type : 'submit',
9320
9321     haveProgress : false,
9322     uploadComplete : false,
9323     
9324     // uploadProgress indicator.
9325     uploadProgress : function()
9326     {
9327         if (!this.form.progressUrl) {
9328             return;
9329         }
9330         
9331         if (!this.haveProgress) {
9332             Roo.MessageBox.progress("Uploading", "Uploading");
9333         }
9334         if (this.uploadComplete) {
9335            Roo.MessageBox.hide();
9336            return;
9337         }
9338         
9339         this.haveProgress = true;
9340    
9341         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9342         
9343         var c = new Roo.data.Connection();
9344         c.request({
9345             url : this.form.progressUrl,
9346             params: {
9347                 id : uid
9348             },
9349             method: 'GET',
9350             success : function(req){
9351                //console.log(data);
9352                 var rdata = false;
9353                 var edata;
9354                 try  {
9355                    rdata = Roo.decode(req.responseText)
9356                 } catch (e) {
9357                     Roo.log("Invalid data from server..");
9358                     Roo.log(edata);
9359                     return;
9360                 }
9361                 if (!rdata || !rdata.success) {
9362                     Roo.log(rdata);
9363                     Roo.MessageBox.alert(Roo.encode(rdata));
9364                     return;
9365                 }
9366                 var data = rdata.data;
9367                 
9368                 if (this.uploadComplete) {
9369                    Roo.MessageBox.hide();
9370                    return;
9371                 }
9372                    
9373                 if (data){
9374                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9375                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9376                     );
9377                 }
9378                 this.uploadProgress.defer(2000,this);
9379             },
9380        
9381             failure: function(data) {
9382                 Roo.log('progress url failed ');
9383                 Roo.log(data);
9384             },
9385             scope : this
9386         });
9387            
9388     },
9389     
9390     
9391     run : function()
9392     {
9393         // run get Values on the form, so it syncs any secondary forms.
9394         this.form.getValues();
9395         
9396         var o = this.options;
9397         var method = this.getMethod();
9398         var isPost = method == 'POST';
9399         if(o.clientValidation === false || this.form.isValid()){
9400             
9401             if (this.form.progressUrl) {
9402                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9403                     (new Date() * 1) + '' + Math.random());
9404                     
9405             } 
9406             
9407             
9408             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9409                 form:this.form.el.dom,
9410                 url:this.getUrl(!isPost),
9411                 method: method,
9412                 params:isPost ? this.getParams() : null,
9413                 isUpload: this.form.fileUpload,
9414                 formData : this.form.formData
9415             }));
9416             
9417             this.uploadProgress();
9418
9419         }else if (o.clientValidation !== false){ // client validation failed
9420             this.failureType = Roo.form.Action.CLIENT_INVALID;
9421             this.form.afterAction(this, false);
9422         }
9423     },
9424
9425     success : function(response)
9426     {
9427         this.uploadComplete= true;
9428         if (this.haveProgress) {
9429             Roo.MessageBox.hide();
9430         }
9431         
9432         
9433         var result = this.processResponse(response);
9434         if(result === true || result.success){
9435             this.form.afterAction(this, true);
9436             return;
9437         }
9438         if(result.errors){
9439             this.form.markInvalid(result.errors);
9440             this.failureType = Roo.form.Action.SERVER_INVALID;
9441         }
9442         this.form.afterAction(this, false);
9443     },
9444     failure : function(response)
9445     {
9446         this.uploadComplete= true;
9447         if (this.haveProgress) {
9448             Roo.MessageBox.hide();
9449         }
9450         
9451         this.response = response;
9452         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9453         this.form.afterAction(this, false);
9454     },
9455     
9456     handleResponse : function(response){
9457         if(this.form.errorReader){
9458             var rs = this.form.errorReader.read(response);
9459             var errors = [];
9460             if(rs.records){
9461                 for(var i = 0, len = rs.records.length; i < len; i++) {
9462                     var r = rs.records[i];
9463                     errors[i] = r.data;
9464                 }
9465             }
9466             if(errors.length < 1){
9467                 errors = null;
9468             }
9469             return {
9470                 success : rs.success,
9471                 errors : errors
9472             };
9473         }
9474         var ret = false;
9475         try {
9476             ret = Roo.decode(response.responseText);
9477         } catch (e) {
9478             ret = {
9479                 success: false,
9480                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9481                 errors : []
9482             };
9483         }
9484         return ret;
9485         
9486     }
9487 });
9488
9489
9490 Roo.form.Action.Load = function(form, options){
9491     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9492     this.reader = this.form.reader;
9493 };
9494
9495 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9496     type : 'load',
9497
9498     run : function(){
9499         
9500         Roo.Ajax.request(Roo.apply(
9501                 this.createCallback(), {
9502                     method:this.getMethod(),
9503                     url:this.getUrl(false),
9504                     params:this.getParams()
9505         }));
9506     },
9507
9508     success : function(response){
9509         
9510         var result = this.processResponse(response);
9511         if(result === true || !result.success || !result.data){
9512             this.failureType = Roo.form.Action.LOAD_FAILURE;
9513             this.form.afterAction(this, false);
9514             return;
9515         }
9516         this.form.clearInvalid();
9517         this.form.setValues(result.data);
9518         this.form.afterAction(this, true);
9519     },
9520
9521     handleResponse : function(response){
9522         if(this.form.reader){
9523             var rs = this.form.reader.read(response);
9524             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9525             return {
9526                 success : rs.success,
9527                 data : data
9528             };
9529         }
9530         return Roo.decode(response.responseText);
9531     }
9532 });
9533
9534 Roo.form.Action.ACTION_TYPES = {
9535     'load' : Roo.form.Action.Load,
9536     'submit' : Roo.form.Action.Submit
9537 };/*
9538  * - LGPL
9539  *
9540  * form
9541  *
9542  */
9543
9544 /**
9545  * @class Roo.bootstrap.Form
9546  * @extends Roo.bootstrap.Component
9547  * Bootstrap Form class
9548  * @cfg {String} method  GET | POST (default POST)
9549  * @cfg {String} labelAlign top | left (default top)
9550  * @cfg {String} align left  | right - for navbars
9551  * @cfg {Boolean} loadMask load mask when submit (default true)
9552
9553  *
9554  * @constructor
9555  * Create a new Form
9556  * @param {Object} config The config object
9557  */
9558
9559
9560 Roo.bootstrap.Form = function(config){
9561     
9562     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9563     
9564     Roo.bootstrap.Form.popover.apply();
9565     
9566     this.addEvents({
9567         /**
9568          * @event clientvalidation
9569          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9570          * @param {Form} this
9571          * @param {Boolean} valid true if the form has passed client-side validation
9572          */
9573         clientvalidation: true,
9574         /**
9575          * @event beforeaction
9576          * Fires before any action is performed. Return false to cancel the action.
9577          * @param {Form} this
9578          * @param {Action} action The action to be performed
9579          */
9580         beforeaction: true,
9581         /**
9582          * @event actionfailed
9583          * Fires when an action fails.
9584          * @param {Form} this
9585          * @param {Action} action The action that failed
9586          */
9587         actionfailed : true,
9588         /**
9589          * @event actioncomplete
9590          * Fires when an action is completed.
9591          * @param {Form} this
9592          * @param {Action} action The action that completed
9593          */
9594         actioncomplete : true
9595     });
9596 };
9597
9598 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9599
9600      /**
9601      * @cfg {String} method
9602      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9603      */
9604     method : 'POST',
9605     /**
9606      * @cfg {String} url
9607      * The URL to use for form actions if one isn't supplied in the action options.
9608      */
9609     /**
9610      * @cfg {Boolean} fileUpload
9611      * Set to true if this form is a file upload.
9612      */
9613
9614     /**
9615      * @cfg {Object} baseParams
9616      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9617      */
9618
9619     /**
9620      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9621      */
9622     timeout: 30,
9623     /**
9624      * @cfg {Sting} align (left|right) for navbar forms
9625      */
9626     align : 'left',
9627
9628     // private
9629     activeAction : null,
9630
9631     /**
9632      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9633      * element by passing it or its id or mask the form itself by passing in true.
9634      * @type Mixed
9635      */
9636     waitMsgTarget : false,
9637
9638     loadMask : true,
9639     
9640     /**
9641      * @cfg {Boolean} errorMask (true|false) default false
9642      */
9643     errorMask : false,
9644     
9645     /**
9646      * @cfg {Number} maskOffset Default 100
9647      */
9648     maskOffset : 100,
9649     
9650     /**
9651      * @cfg {Boolean} maskBody
9652      */
9653     maskBody : false,
9654
9655     getAutoCreate : function(){
9656
9657         var cfg = {
9658             tag: 'form',
9659             method : this.method || 'POST',
9660             id : this.id || Roo.id(),
9661             cls : ''
9662         };
9663         if (this.parent().xtype.match(/^Nav/)) {
9664             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9665
9666         }
9667
9668         if (this.labelAlign == 'left' ) {
9669             cfg.cls += ' form-horizontal';
9670         }
9671
9672
9673         return cfg;
9674     },
9675     initEvents : function()
9676     {
9677         this.el.on('submit', this.onSubmit, this);
9678         // this was added as random key presses on the form where triggering form submit.
9679         this.el.on('keypress', function(e) {
9680             if (e.getCharCode() != 13) {
9681                 return true;
9682             }
9683             // we might need to allow it for textareas.. and some other items.
9684             // check e.getTarget().
9685
9686             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9687                 return true;
9688             }
9689
9690             Roo.log("keypress blocked");
9691
9692             e.preventDefault();
9693             return false;
9694         });
9695         
9696     },
9697     // private
9698     onSubmit : function(e){
9699         e.stopEvent();
9700     },
9701
9702      /**
9703      * Returns true if client-side validation on the form is successful.
9704      * @return Boolean
9705      */
9706     isValid : function(){
9707         var items = this.getItems();
9708         var valid = true;
9709         var target = false;
9710         
9711         items.each(function(f){
9712             
9713             if(f.validate()){
9714                 return;
9715             }
9716             
9717             Roo.log('invalid field: ' + f.name);
9718             
9719             valid = false;
9720
9721             if(!target && f.el.isVisible(true)){
9722                 target = f;
9723             }
9724            
9725         });
9726         
9727         if(this.errorMask && !valid){
9728             Roo.bootstrap.Form.popover.mask(this, target);
9729         }
9730         
9731         return valid;
9732     },
9733     
9734     /**
9735      * Returns true if any fields in this form have changed since their original load.
9736      * @return Boolean
9737      */
9738     isDirty : function(){
9739         var dirty = false;
9740         var items = this.getItems();
9741         items.each(function(f){
9742            if(f.isDirty()){
9743                dirty = true;
9744                return false;
9745            }
9746            return true;
9747         });
9748         return dirty;
9749     },
9750      /**
9751      * Performs a predefined action (submit or load) or custom actions you define on this form.
9752      * @param {String} actionName The name of the action type
9753      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9754      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9755      * accept other config options):
9756      * <pre>
9757 Property          Type             Description
9758 ----------------  ---------------  ----------------------------------------------------------------------------------
9759 url               String           The url for the action (defaults to the form's url)
9760 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9761 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9762 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9763                                    validate the form on the client (defaults to false)
9764      * </pre>
9765      * @return {BasicForm} this
9766      */
9767     doAction : function(action, options){
9768         if(typeof action == 'string'){
9769             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9770         }
9771         if(this.fireEvent('beforeaction', this, action) !== false){
9772             this.beforeAction(action);
9773             action.run.defer(100, action);
9774         }
9775         return this;
9776     },
9777
9778     // private
9779     beforeAction : function(action){
9780         var o = action.options;
9781         
9782         if(this.loadMask){
9783             
9784             if(this.maskBody){
9785                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9786             } else {
9787                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9788             }
9789         }
9790         // not really supported yet.. ??
9791
9792         //if(this.waitMsgTarget === true){
9793         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9794         //}else if(this.waitMsgTarget){
9795         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9796         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9797         //}else {
9798         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9799        // }
9800
9801     },
9802
9803     // private
9804     afterAction : function(action, success){
9805         this.activeAction = null;
9806         var o = action.options;
9807
9808         if(this.loadMask){
9809             
9810             if(this.maskBody){
9811                 Roo.get(document.body).unmask();
9812             } else {
9813                 this.el.unmask();
9814             }
9815         }
9816         
9817         //if(this.waitMsgTarget === true){
9818 //            this.el.unmask();
9819         //}else if(this.waitMsgTarget){
9820         //    this.waitMsgTarget.unmask();
9821         //}else{
9822         //    Roo.MessageBox.updateProgress(1);
9823         //    Roo.MessageBox.hide();
9824        // }
9825         //
9826         if(success){
9827             if(o.reset){
9828                 this.reset();
9829             }
9830             Roo.callback(o.success, o.scope, [this, action]);
9831             this.fireEvent('actioncomplete', this, action);
9832
9833         }else{
9834
9835             // failure condition..
9836             // we have a scenario where updates need confirming.
9837             // eg. if a locking scenario exists..
9838             // we look for { errors : { needs_confirm : true }} in the response.
9839             if (
9840                 (typeof(action.result) != 'undefined')  &&
9841                 (typeof(action.result.errors) != 'undefined')  &&
9842                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9843            ){
9844                 var _t = this;
9845                 Roo.log("not supported yet");
9846                  /*
9847
9848                 Roo.MessageBox.confirm(
9849                     "Change requires confirmation",
9850                     action.result.errorMsg,
9851                     function(r) {
9852                         if (r != 'yes') {
9853                             return;
9854                         }
9855                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9856                     }
9857
9858                 );
9859                 */
9860
9861
9862                 return;
9863             }
9864
9865             Roo.callback(o.failure, o.scope, [this, action]);
9866             // show an error message if no failed handler is set..
9867             if (!this.hasListener('actionfailed')) {
9868                 Roo.log("need to add dialog support");
9869                 /*
9870                 Roo.MessageBox.alert("Error",
9871                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9872                         action.result.errorMsg :
9873                         "Saving Failed, please check your entries or try again"
9874                 );
9875                 */
9876             }
9877
9878             this.fireEvent('actionfailed', this, action);
9879         }
9880
9881     },
9882     /**
9883      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9884      * @param {String} id The value to search for
9885      * @return Field
9886      */
9887     findField : function(id){
9888         var items = this.getItems();
9889         var field = items.get(id);
9890         if(!field){
9891              items.each(function(f){
9892                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9893                     field = f;
9894                     return false;
9895                 }
9896                 return true;
9897             });
9898         }
9899         return field || null;
9900     },
9901      /**
9902      * Mark fields in this form invalid in bulk.
9903      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9904      * @return {BasicForm} this
9905      */
9906     markInvalid : function(errors){
9907         if(errors instanceof Array){
9908             for(var i = 0, len = errors.length; i < len; i++){
9909                 var fieldError = errors[i];
9910                 var f = this.findField(fieldError.id);
9911                 if(f){
9912                     f.markInvalid(fieldError.msg);
9913                 }
9914             }
9915         }else{
9916             var field, id;
9917             for(id in errors){
9918                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9919                     field.markInvalid(errors[id]);
9920                 }
9921             }
9922         }
9923         //Roo.each(this.childForms || [], function (f) {
9924         //    f.markInvalid(errors);
9925         //});
9926
9927         return this;
9928     },
9929
9930     /**
9931      * Set values for fields in this form in bulk.
9932      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9933      * @return {BasicForm} this
9934      */
9935     setValues : function(values){
9936         if(values instanceof Array){ // array of objects
9937             for(var i = 0, len = values.length; i < len; i++){
9938                 var v = values[i];
9939                 var f = this.findField(v.id);
9940                 if(f){
9941                     f.setValue(v.value);
9942                     if(this.trackResetOnLoad){
9943                         f.originalValue = f.getValue();
9944                     }
9945                 }
9946             }
9947         }else{ // object hash
9948             var field, id;
9949             for(id in values){
9950                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9951
9952                     if (field.setFromData &&
9953                         field.valueField &&
9954                         field.displayField &&
9955                         // combos' with local stores can
9956                         // be queried via setValue()
9957                         // to set their value..
9958                         (field.store && !field.store.isLocal)
9959                         ) {
9960                         // it's a combo
9961                         var sd = { };
9962                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9963                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9964                         field.setFromData(sd);
9965
9966                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9967                         
9968                         field.setFromData(values);
9969                         
9970                     } else {
9971                         field.setValue(values[id]);
9972                     }
9973
9974
9975                     if(this.trackResetOnLoad){
9976                         field.originalValue = field.getValue();
9977                     }
9978                 }
9979             }
9980         }
9981
9982         //Roo.each(this.childForms || [], function (f) {
9983         //    f.setValues(values);
9984         //});
9985
9986         return this;
9987     },
9988
9989     /**
9990      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9991      * they are returned as an array.
9992      * @param {Boolean} asString
9993      * @return {Object}
9994      */
9995     getValues : function(asString){
9996         //if (this.childForms) {
9997             // copy values from the child forms
9998         //    Roo.each(this.childForms, function (f) {
9999         //        this.setValues(f.getValues());
10000         //    }, this);
10001         //}
10002
10003
10004
10005         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10006         if(asString === true){
10007             return fs;
10008         }
10009         return Roo.urlDecode(fs);
10010     },
10011
10012     /**
10013      * Returns the fields in this form as an object with key/value pairs.
10014      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10015      * @return {Object}
10016      */
10017     getFieldValues : function(with_hidden)
10018     {
10019         var items = this.getItems();
10020         var ret = {};
10021         items.each(function(f){
10022             
10023             if (!f.getName()) {
10024                 return;
10025             }
10026             
10027             var v = f.getValue();
10028             
10029             if (f.inputType =='radio') {
10030                 if (typeof(ret[f.getName()]) == 'undefined') {
10031                     ret[f.getName()] = ''; // empty..
10032                 }
10033
10034                 if (!f.el.dom.checked) {
10035                     return;
10036
10037                 }
10038                 v = f.el.dom.value;
10039
10040             }
10041             
10042             if(f.xtype == 'MoneyField'){
10043                 ret[f.currencyName] = f.getCurrency();
10044             }
10045
10046             // not sure if this supported any more..
10047             if ((typeof(v) == 'object') && f.getRawValue) {
10048                 v = f.getRawValue() ; // dates..
10049             }
10050             // combo boxes where name != hiddenName...
10051             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10052                 ret[f.name] = f.getRawValue();
10053             }
10054             ret[f.getName()] = v;
10055         });
10056
10057         return ret;
10058     },
10059
10060     /**
10061      * Clears all invalid messages in this form.
10062      * @return {BasicForm} this
10063      */
10064     clearInvalid : function(){
10065         var items = this.getItems();
10066
10067         items.each(function(f){
10068            f.clearInvalid();
10069         });
10070
10071         return this;
10072     },
10073
10074     /**
10075      * Resets this form.
10076      * @return {BasicForm} this
10077      */
10078     reset : function(){
10079         var items = this.getItems();
10080         items.each(function(f){
10081             f.reset();
10082         });
10083
10084         Roo.each(this.childForms || [], function (f) {
10085             f.reset();
10086         });
10087
10088
10089         return this;
10090     },
10091     
10092     getItems : function()
10093     {
10094         var r=new Roo.util.MixedCollection(false, function(o){
10095             return o.id || (o.id = Roo.id());
10096         });
10097         var iter = function(el) {
10098             if (el.inputEl) {
10099                 r.add(el);
10100             }
10101             if (!el.items) {
10102                 return;
10103             }
10104             Roo.each(el.items,function(e) {
10105                 iter(e);
10106             });
10107         };
10108
10109         iter(this);
10110         return r;
10111     },
10112     
10113     hideFields : function(items)
10114     {
10115         Roo.each(items, function(i){
10116             
10117             var f = this.findField(i);
10118             
10119             if(!f){
10120                 return;
10121             }
10122             
10123             f.hide();
10124             
10125         }, this);
10126     },
10127     
10128     showFields : function(items)
10129     {
10130         Roo.each(items, function(i){
10131             
10132             var f = this.findField(i);
10133             
10134             if(!f){
10135                 return;
10136             }
10137             
10138             f.show();
10139             
10140         }, this);
10141     }
10142
10143 });
10144
10145 Roo.apply(Roo.bootstrap.Form, {
10146     
10147     popover : {
10148         
10149         padding : 5,
10150         
10151         isApplied : false,
10152         
10153         isMasked : false,
10154         
10155         form : false,
10156         
10157         target : false,
10158         
10159         toolTip : false,
10160         
10161         intervalID : false,
10162         
10163         maskEl : false,
10164         
10165         apply : function()
10166         {
10167             if(this.isApplied){
10168                 return;
10169             }
10170             
10171             this.maskEl = {
10172                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10173                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10174                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10175                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10176             };
10177             
10178             this.maskEl.top.enableDisplayMode("block");
10179             this.maskEl.left.enableDisplayMode("block");
10180             this.maskEl.bottom.enableDisplayMode("block");
10181             this.maskEl.right.enableDisplayMode("block");
10182             
10183             this.toolTip = new Roo.bootstrap.Tooltip({
10184                 cls : 'roo-form-error-popover',
10185                 alignment : {
10186                     'left' : ['r-l', [-2,0], 'right'],
10187                     'right' : ['l-r', [2,0], 'left'],
10188                     'bottom' : ['tl-bl', [0,2], 'top'],
10189                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10190                 }
10191             });
10192             
10193             this.toolTip.render(Roo.get(document.body));
10194
10195             this.toolTip.el.enableDisplayMode("block");
10196             
10197             Roo.get(document.body).on('click', function(){
10198                 this.unmask();
10199             }, this);
10200             
10201             Roo.get(document.body).on('touchstart', function(){
10202                 this.unmask();
10203             }, this);
10204             
10205             this.isApplied = true
10206         },
10207         
10208         mask : function(form, target)
10209         {
10210             this.form = form;
10211             
10212             this.target = target;
10213             
10214             if(!this.form.errorMask || !target.el){
10215                 return;
10216             }
10217             
10218             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10219             
10220             Roo.log(scrollable);
10221             
10222             var ot = this.target.el.calcOffsetsTo(scrollable);
10223             
10224             var scrollTo = ot[1] - this.form.maskOffset;
10225             
10226             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10227             
10228             scrollable.scrollTo('top', scrollTo);
10229             
10230             var box = this.target.el.getBox();
10231             Roo.log(box);
10232             var zIndex = Roo.bootstrap.Modal.zIndex++;
10233
10234             
10235             this.maskEl.top.setStyle('position', 'absolute');
10236             this.maskEl.top.setStyle('z-index', zIndex);
10237             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10238             this.maskEl.top.setLeft(0);
10239             this.maskEl.top.setTop(0);
10240             this.maskEl.top.show();
10241             
10242             this.maskEl.left.setStyle('position', 'absolute');
10243             this.maskEl.left.setStyle('z-index', zIndex);
10244             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10245             this.maskEl.left.setLeft(0);
10246             this.maskEl.left.setTop(box.y - this.padding);
10247             this.maskEl.left.show();
10248
10249             this.maskEl.bottom.setStyle('position', 'absolute');
10250             this.maskEl.bottom.setStyle('z-index', zIndex);
10251             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10252             this.maskEl.bottom.setLeft(0);
10253             this.maskEl.bottom.setTop(box.bottom + this.padding);
10254             this.maskEl.bottom.show();
10255
10256             this.maskEl.right.setStyle('position', 'absolute');
10257             this.maskEl.right.setStyle('z-index', zIndex);
10258             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10259             this.maskEl.right.setLeft(box.right + this.padding);
10260             this.maskEl.right.setTop(box.y - this.padding);
10261             this.maskEl.right.show();
10262
10263             this.toolTip.bindEl = this.target.el;
10264
10265             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10266
10267             var tip = this.target.blankText;
10268
10269             if(this.target.getValue() !== '' ) {
10270                 
10271                 if (this.target.invalidText.length) {
10272                     tip = this.target.invalidText;
10273                 } else if (this.target.regexText.length){
10274                     tip = this.target.regexText;
10275                 }
10276             }
10277
10278             this.toolTip.show(tip);
10279
10280             this.intervalID = window.setInterval(function() {
10281                 Roo.bootstrap.Form.popover.unmask();
10282             }, 10000);
10283
10284             window.onwheel = function(){ return false;};
10285             
10286             (function(){ this.isMasked = true; }).defer(500, this);
10287             
10288         },
10289         
10290         unmask : function()
10291         {
10292             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10293                 return;
10294             }
10295             
10296             this.maskEl.top.setStyle('position', 'absolute');
10297             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10298             this.maskEl.top.hide();
10299
10300             this.maskEl.left.setStyle('position', 'absolute');
10301             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10302             this.maskEl.left.hide();
10303
10304             this.maskEl.bottom.setStyle('position', 'absolute');
10305             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10306             this.maskEl.bottom.hide();
10307
10308             this.maskEl.right.setStyle('position', 'absolute');
10309             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10310             this.maskEl.right.hide();
10311             
10312             this.toolTip.hide();
10313             
10314             this.toolTip.el.hide();
10315             
10316             window.onwheel = function(){ return true;};
10317             
10318             if(this.intervalID){
10319                 window.clearInterval(this.intervalID);
10320                 this.intervalID = false;
10321             }
10322             
10323             this.isMasked = false;
10324             
10325         }
10326         
10327     }
10328     
10329 });
10330
10331 /*
10332  * Based on:
10333  * Ext JS Library 1.1.1
10334  * Copyright(c) 2006-2007, Ext JS, LLC.
10335  *
10336  * Originally Released Under LGPL - original licence link has changed is not relivant.
10337  *
10338  * Fork - LGPL
10339  * <script type="text/javascript">
10340  */
10341 /**
10342  * @class Roo.form.VTypes
10343  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10344  * @singleton
10345  */
10346 Roo.form.VTypes = function(){
10347     // closure these in so they are only created once.
10348     var alpha = /^[a-zA-Z_]+$/;
10349     var alphanum = /^[a-zA-Z0-9_]+$/;
10350     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10351     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10352
10353     // All these messages and functions are configurable
10354     return {
10355         /**
10356          * The function used to validate email addresses
10357          * @param {String} value The email address
10358          */
10359         'email' : function(v){
10360             return email.test(v);
10361         },
10362         /**
10363          * The error text to display when the email validation function returns false
10364          * @type String
10365          */
10366         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10367         /**
10368          * The keystroke filter mask to be applied on email input
10369          * @type RegExp
10370          */
10371         'emailMask' : /[a-z0-9_\.\-@]/i,
10372
10373         /**
10374          * The function used to validate URLs
10375          * @param {String} value The URL
10376          */
10377         'url' : function(v){
10378             return url.test(v);
10379         },
10380         /**
10381          * The error text to display when the url validation function returns false
10382          * @type String
10383          */
10384         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10385         
10386         /**
10387          * The function used to validate alpha values
10388          * @param {String} value The value
10389          */
10390         'alpha' : function(v){
10391             return alpha.test(v);
10392         },
10393         /**
10394          * The error text to display when the alpha validation function returns false
10395          * @type String
10396          */
10397         'alphaText' : 'This field should only contain letters and _',
10398         /**
10399          * The keystroke filter mask to be applied on alpha input
10400          * @type RegExp
10401          */
10402         'alphaMask' : /[a-z_]/i,
10403
10404         /**
10405          * The function used to validate alphanumeric values
10406          * @param {String} value The value
10407          */
10408         'alphanum' : function(v){
10409             return alphanum.test(v);
10410         },
10411         /**
10412          * The error text to display when the alphanumeric validation function returns false
10413          * @type String
10414          */
10415         'alphanumText' : 'This field should only contain letters, numbers and _',
10416         /**
10417          * The keystroke filter mask to be applied on alphanumeric input
10418          * @type RegExp
10419          */
10420         'alphanumMask' : /[a-z0-9_]/i
10421     };
10422 }();/*
10423  * - LGPL
10424  *
10425  * Input
10426  * 
10427  */
10428
10429 /**
10430  * @class Roo.bootstrap.Input
10431  * @extends Roo.bootstrap.Component
10432  * Bootstrap Input class
10433  * @cfg {Boolean} disabled is it disabled
10434  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10435  * @cfg {String} name name of the input
10436  * @cfg {string} fieldLabel - the label associated
10437  * @cfg {string} placeholder - placeholder to put in text.
10438  * @cfg {string}  before - input group add on before
10439  * @cfg {string} after - input group add on after
10440  * @cfg {string} size - (lg|sm) or leave empty..
10441  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10442  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10443  * @cfg {Number} md colspan out of 12 for computer-sized screens
10444  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10445  * @cfg {string} value default value of the input
10446  * @cfg {Number} labelWidth set the width of label 
10447  * @cfg {Number} labellg set the width of label (1-12)
10448  * @cfg {Number} labelmd set the width of label (1-12)
10449  * @cfg {Number} labelsm set the width of label (1-12)
10450  * @cfg {Number} labelxs set the width of label (1-12)
10451  * @cfg {String} labelAlign (top|left)
10452  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10453  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10454  * @cfg {String} indicatorpos (left|right) default left
10455  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10456  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10457  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10458
10459  * @cfg {String} align (left|center|right) Default left
10460  * @cfg {Boolean} forceFeedback (true|false) Default false
10461  * 
10462  * @constructor
10463  * Create a new Input
10464  * @param {Object} config The config object
10465  */
10466
10467 Roo.bootstrap.Input = function(config){
10468     
10469     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10470     
10471     this.addEvents({
10472         /**
10473          * @event focus
10474          * Fires when this field receives input focus.
10475          * @param {Roo.form.Field} this
10476          */
10477         focus : true,
10478         /**
10479          * @event blur
10480          * Fires when this field loses input focus.
10481          * @param {Roo.form.Field} this
10482          */
10483         blur : true,
10484         /**
10485          * @event specialkey
10486          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10487          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject} e The event object
10490          */
10491         specialkey : true,
10492         /**
10493          * @event change
10494          * Fires just before the field blurs if the field value has changed.
10495          * @param {Roo.form.Field} this
10496          * @param {Mixed} newValue The new value
10497          * @param {Mixed} oldValue The original value
10498          */
10499         change : true,
10500         /**
10501          * @event invalid
10502          * Fires after the field has been marked as invalid.
10503          * @param {Roo.form.Field} this
10504          * @param {String} msg The validation message
10505          */
10506         invalid : true,
10507         /**
10508          * @event valid
10509          * Fires after the field has been validated with no errors.
10510          * @param {Roo.form.Field} this
10511          */
10512         valid : true,
10513          /**
10514          * @event keyup
10515          * Fires after the key up
10516          * @param {Roo.form.Field} this
10517          * @param {Roo.EventObject}  e The event Object
10518          */
10519         keyup : true,
10520         /**
10521          * @event paste
10522          * Fires after the user pastes into input
10523          * @param {Roo.form.Field} this
10524          * @param {Roo.EventObject}  e The event Object
10525          */
10526         paste : true
10527     });
10528 };
10529
10530 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10531      /**
10532      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10533       automatic validation (defaults to "keyup").
10534      */
10535     validationEvent : "keyup",
10536      /**
10537      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10538      */
10539     validateOnBlur : true,
10540     /**
10541      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10542      */
10543     validationDelay : 250,
10544      /**
10545      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10546      */
10547     focusClass : "x-form-focus",  // not needed???
10548     
10549        
10550     /**
10551      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10552      */
10553     invalidClass : "has-warning",
10554     
10555     /**
10556      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10557      */
10558     validClass : "has-success",
10559     
10560     /**
10561      * @cfg {Boolean} hasFeedback (true|false) default true
10562      */
10563     hasFeedback : true,
10564     
10565     /**
10566      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10567      */
10568     invalidFeedbackClass : "glyphicon-warning-sign",
10569     
10570     /**
10571      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10572      */
10573     validFeedbackClass : "glyphicon-ok",
10574     
10575     /**
10576      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10577      */
10578     selectOnFocus : false,
10579     
10580      /**
10581      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10582      */
10583     maskRe : null,
10584        /**
10585      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10586      */
10587     vtype : null,
10588     
10589       /**
10590      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10591      */
10592     disableKeyFilter : false,
10593     
10594        /**
10595      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10596      */
10597     disabled : false,
10598      /**
10599      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10600      */
10601     allowBlank : true,
10602     /**
10603      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10604      */
10605     blankText : "Please complete this mandatory field",
10606     
10607      /**
10608      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10609      */
10610     minLength : 0,
10611     /**
10612      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10613      */
10614     maxLength : Number.MAX_VALUE,
10615     /**
10616      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10617      */
10618     minLengthText : "The minimum length for this field is {0}",
10619     /**
10620      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10621      */
10622     maxLengthText : "The maximum length for this field is {0}",
10623   
10624     
10625     /**
10626      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10627      * If available, this function will be called only after the basic validators all return true, and will be passed the
10628      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10629      */
10630     validator : null,
10631     /**
10632      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10633      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10634      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10635      */
10636     regex : null,
10637     /**
10638      * @cfg {String} regexText -- Depricated - use Invalid Text
10639      */
10640     regexText : "",
10641     
10642     /**
10643      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10644      */
10645     invalidText : "",
10646     
10647     
10648     
10649     autocomplete: false,
10650     
10651     
10652     fieldLabel : '',
10653     inputType : 'text',
10654     
10655     name : false,
10656     placeholder: false,
10657     before : false,
10658     after : false,
10659     size : false,
10660     hasFocus : false,
10661     preventMark: false,
10662     isFormField : true,
10663     value : '',
10664     labelWidth : 2,
10665     labelAlign : false,
10666     readOnly : false,
10667     align : false,
10668     formatedValue : false,
10669     forceFeedback : false,
10670     
10671     indicatorpos : 'left',
10672     
10673     labellg : 0,
10674     labelmd : 0,
10675     labelsm : 0,
10676     labelxs : 0,
10677     
10678     capture : '',
10679     accept : '',
10680     
10681     parentLabelAlign : function()
10682     {
10683         var parent = this;
10684         while (parent.parent()) {
10685             parent = parent.parent();
10686             if (typeof(parent.labelAlign) !='undefined') {
10687                 return parent.labelAlign;
10688             }
10689         }
10690         return 'left';
10691         
10692     },
10693     
10694     getAutoCreate : function()
10695     {
10696         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10697         
10698         var id = Roo.id();
10699         
10700         var cfg = {};
10701         
10702         if(this.inputType != 'hidden'){
10703             cfg.cls = 'form-group' //input-group
10704         }
10705         
10706         var input =  {
10707             tag: 'input',
10708             id : id,
10709             type : this.inputType,
10710             value : this.value,
10711             cls : 'form-control',
10712             placeholder : this.placeholder || '',
10713             autocomplete : this.autocomplete || 'new-password'
10714         };
10715         if (this.inputType == 'file') {
10716             input.style = 'overflow:hidden'; // why not in CSS?
10717         }
10718         
10719         if(this.capture.length){
10720             input.capture = this.capture;
10721         }
10722         
10723         if(this.accept.length){
10724             input.accept = this.accept + "/*";
10725         }
10726         
10727         if(this.align){
10728             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10729         }
10730         
10731         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10732             input.maxLength = this.maxLength;
10733         }
10734         
10735         if (this.disabled) {
10736             input.disabled=true;
10737         }
10738         
10739         if (this.readOnly) {
10740             input.readonly=true;
10741         }
10742         
10743         if (this.name) {
10744             input.name = this.name;
10745         }
10746         
10747         if (this.size) {
10748             input.cls += ' input-' + this.size;
10749         }
10750         
10751         var settings=this;
10752         ['xs','sm','md','lg'].map(function(size){
10753             if (settings[size]) {
10754                 cfg.cls += ' col-' + size + '-' + settings[size];
10755             }
10756         });
10757         
10758         var inputblock = input;
10759         
10760         var feedback = {
10761             tag: 'span',
10762             cls: 'glyphicon form-control-feedback'
10763         };
10764             
10765         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10766             
10767             inputblock = {
10768                 cls : 'has-feedback',
10769                 cn :  [
10770                     input,
10771                     feedback
10772                 ] 
10773             };  
10774         }
10775         
10776         if (this.before || this.after) {
10777             
10778             inputblock = {
10779                 cls : 'input-group',
10780                 cn :  [] 
10781             };
10782             
10783             if (this.before && typeof(this.before) == 'string') {
10784                 
10785                 inputblock.cn.push({
10786                     tag :'span',
10787                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10788                     html : this.before
10789                 });
10790             }
10791             if (this.before && typeof(this.before) == 'object') {
10792                 this.before = Roo.factory(this.before);
10793                 
10794                 inputblock.cn.push({
10795                     tag :'span',
10796                     cls : 'roo-input-before input-group-prepend   input-group-' +
10797                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10798                 });
10799             }
10800             
10801             inputblock.cn.push(input);
10802             
10803             if (this.after && typeof(this.after) == 'string') {
10804                 inputblock.cn.push({
10805                     tag :'span',
10806                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10807                     html : this.after
10808                 });
10809             }
10810             if (this.after && typeof(this.after) == 'object') {
10811                 this.after = Roo.factory(this.after);
10812                 
10813                 inputblock.cn.push({
10814                     tag :'span',
10815                     cls : 'roo-input-after input-group-append  input-group-' +
10816                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10817                 });
10818             }
10819             
10820             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10821                 inputblock.cls += ' has-feedback';
10822                 inputblock.cn.push(feedback);
10823             }
10824         };
10825         var indicator = {
10826             tag : 'i',
10827             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10828             tooltip : 'This field is required'
10829         };
10830         if (this.allowBlank ) {
10831             indicator.style = this.allowBlank ? ' display:none' : '';
10832         }
10833         if (align ==='left' && this.fieldLabel.length) {
10834             
10835             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10836             
10837             cfg.cn = [
10838                 indicator,
10839                 {
10840                     tag: 'label',
10841                     'for' :  id,
10842                     cls : 'control-label col-form-label',
10843                     html : this.fieldLabel
10844
10845                 },
10846                 {
10847                     cls : "", 
10848                     cn: [
10849                         inputblock
10850                     ]
10851                 }
10852             ];
10853             
10854             var labelCfg = cfg.cn[1];
10855             var contentCfg = cfg.cn[2];
10856             
10857             if(this.indicatorpos == 'right'){
10858                 cfg.cn = [
10859                     {
10860                         tag: 'label',
10861                         'for' :  id,
10862                         cls : 'control-label col-form-label',
10863                         cn : [
10864                             {
10865                                 tag : 'span',
10866                                 html : this.fieldLabel
10867                             },
10868                             indicator
10869                         ]
10870                     },
10871                     {
10872                         cls : "",
10873                         cn: [
10874                             inputblock
10875                         ]
10876                     }
10877
10878                 ];
10879                 
10880                 labelCfg = cfg.cn[0];
10881                 contentCfg = cfg.cn[1];
10882             
10883             }
10884             
10885             if(this.labelWidth > 12){
10886                 labelCfg.style = "width: " + this.labelWidth + 'px';
10887             }
10888             
10889             if(this.labelWidth < 13 && this.labelmd == 0){
10890                 this.labelmd = this.labelWidth;
10891             }
10892             
10893             if(this.labellg > 0){
10894                 labelCfg.cls += ' col-lg-' + this.labellg;
10895                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10896             }
10897             
10898             if(this.labelmd > 0){
10899                 labelCfg.cls += ' col-md-' + this.labelmd;
10900                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10901             }
10902             
10903             if(this.labelsm > 0){
10904                 labelCfg.cls += ' col-sm-' + this.labelsm;
10905                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10906             }
10907             
10908             if(this.labelxs > 0){
10909                 labelCfg.cls += ' col-xs-' + this.labelxs;
10910                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10911             }
10912             
10913             
10914         } else if ( this.fieldLabel.length) {
10915                 
10916             
10917             
10918             cfg.cn = [
10919                 {
10920                     tag : 'i',
10921                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10922                     tooltip : 'This field is required',
10923                     style : this.allowBlank ? ' display:none' : '' 
10924                 },
10925                 {
10926                     tag: 'label',
10927                    //cls : 'input-group-addon',
10928                     html : this.fieldLabel
10929
10930                 },
10931
10932                inputblock
10933
10934            ];
10935            
10936            if(this.indicatorpos == 'right'){
10937        
10938                 cfg.cn = [
10939                     {
10940                         tag: 'label',
10941                        //cls : 'input-group-addon',
10942                         html : this.fieldLabel
10943
10944                     },
10945                     {
10946                         tag : 'i',
10947                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10948                         tooltip : 'This field is required',
10949                         style : this.allowBlank ? ' display:none' : '' 
10950                     },
10951
10952                    inputblock
10953
10954                ];
10955
10956             }
10957
10958         } else {
10959             
10960             cfg.cn = [
10961
10962                     inputblock
10963
10964             ];
10965                 
10966                 
10967         };
10968         
10969         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10970            cfg.cls += ' navbar-form';
10971         }
10972         
10973         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10974             // on BS4 we do this only if not form 
10975             cfg.cls += ' navbar-form';
10976             cfg.tag = 'li';
10977         }
10978         
10979         return cfg;
10980         
10981     },
10982     /**
10983      * return the real input element.
10984      */
10985     inputEl: function ()
10986     {
10987         return this.el.select('input.form-control',true).first();
10988     },
10989     
10990     tooltipEl : function()
10991     {
10992         return this.inputEl();
10993     },
10994     
10995     indicatorEl : function()
10996     {
10997         if (Roo.bootstrap.version == 4) {
10998             return false; // not enabled in v4 yet.
10999         }
11000         
11001         var indicator = this.el.select('i.roo-required-indicator',true).first();
11002         
11003         if(!indicator){
11004             return false;
11005         }
11006         
11007         return indicator;
11008         
11009     },
11010     
11011     setDisabled : function(v)
11012     {
11013         var i  = this.inputEl().dom;
11014         if (!v) {
11015             i.removeAttribute('disabled');
11016             return;
11017             
11018         }
11019         i.setAttribute('disabled','true');
11020     },
11021     initEvents : function()
11022     {
11023           
11024         this.inputEl().on("keydown" , this.fireKey,  this);
11025         this.inputEl().on("focus", this.onFocus,  this);
11026         this.inputEl().on("blur", this.onBlur,  this);
11027         
11028         this.inputEl().relayEvent('keyup', this);
11029         this.inputEl().relayEvent('paste', this);
11030         
11031         this.indicator = this.indicatorEl();
11032         
11033         if(this.indicator){
11034             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11035         }
11036  
11037         // reference to original value for reset
11038         this.originalValue = this.getValue();
11039         //Roo.form.TextField.superclass.initEvents.call(this);
11040         if(this.validationEvent == 'keyup'){
11041             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11042             this.inputEl().on('keyup', this.filterValidation, this);
11043         }
11044         else if(this.validationEvent !== false){
11045             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11046         }
11047         
11048         if(this.selectOnFocus){
11049             this.on("focus", this.preFocus, this);
11050             
11051         }
11052         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11053             this.inputEl().on("keypress", this.filterKeys, this);
11054         } else {
11055             this.inputEl().relayEvent('keypress', this);
11056         }
11057        /* if(this.grow){
11058             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11059             this.el.on("click", this.autoSize,  this);
11060         }
11061         */
11062         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11063             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11064         }
11065         
11066         if (typeof(this.before) == 'object') {
11067             this.before.render(this.el.select('.roo-input-before',true).first());
11068         }
11069         if (typeof(this.after) == 'object') {
11070             this.after.render(this.el.select('.roo-input-after',true).first());
11071         }
11072         
11073         this.inputEl().on('change', this.onChange, this);
11074         
11075     },
11076     filterValidation : function(e){
11077         if(!e.isNavKeyPress()){
11078             this.validationTask.delay(this.validationDelay);
11079         }
11080     },
11081      /**
11082      * Validates the field value
11083      * @return {Boolean} True if the value is valid, else false
11084      */
11085     validate : function(){
11086         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11087         if(this.disabled || this.validateValue(this.getRawValue())){
11088             this.markValid();
11089             return true;
11090         }
11091         
11092         this.markInvalid();
11093         return false;
11094     },
11095     
11096     
11097     /**
11098      * Validates a value according to the field's validation rules and marks the field as invalid
11099      * if the validation fails
11100      * @param {Mixed} value The value to validate
11101      * @return {Boolean} True if the value is valid, else false
11102      */
11103     validateValue : function(value)
11104     {
11105         if(this.getVisibilityEl().hasClass('hidden')){
11106             return true;
11107         }
11108         
11109         if(value.length < 1)  { // if it's blank
11110             if(this.allowBlank){
11111                 return true;
11112             }
11113             return false;
11114         }
11115         
11116         if(value.length < this.minLength){
11117             return false;
11118         }
11119         if(value.length > this.maxLength){
11120             return false;
11121         }
11122         if(this.vtype){
11123             var vt = Roo.form.VTypes;
11124             if(!vt[this.vtype](value, this)){
11125                 return false;
11126             }
11127         }
11128         if(typeof this.validator == "function"){
11129             var msg = this.validator(value);
11130             if(msg !== true){
11131                 return false;
11132             }
11133             if (typeof(msg) == 'string') {
11134                 this.invalidText = msg;
11135             }
11136         }
11137         
11138         if(this.regex && !this.regex.test(value)){
11139             return false;
11140         }
11141         
11142         return true;
11143     },
11144     
11145      // private
11146     fireKey : function(e){
11147         //Roo.log('field ' + e.getKey());
11148         if(e.isNavKeyPress()){
11149             this.fireEvent("specialkey", this, e);
11150         }
11151     },
11152     focus : function (selectText){
11153         if(this.rendered){
11154             this.inputEl().focus();
11155             if(selectText === true){
11156                 this.inputEl().dom.select();
11157             }
11158         }
11159         return this;
11160     } ,
11161     
11162     onFocus : function(){
11163         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11164            // this.el.addClass(this.focusClass);
11165         }
11166         if(!this.hasFocus){
11167             this.hasFocus = true;
11168             this.startValue = this.getValue();
11169             this.fireEvent("focus", this);
11170         }
11171     },
11172     
11173     beforeBlur : Roo.emptyFn,
11174
11175     
11176     // private
11177     onBlur : function(){
11178         this.beforeBlur();
11179         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11180             //this.el.removeClass(this.focusClass);
11181         }
11182         this.hasFocus = false;
11183         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11184             this.validate();
11185         }
11186         var v = this.getValue();
11187         if(String(v) !== String(this.startValue)){
11188             this.fireEvent('change', this, v, this.startValue);
11189         }
11190         this.fireEvent("blur", this);
11191     },
11192     
11193     onChange : function(e)
11194     {
11195         var v = this.getValue();
11196         if(String(v) !== String(this.startValue)){
11197             this.fireEvent('change', this, v, this.startValue);
11198         }
11199         
11200     },
11201     
11202     /**
11203      * Resets the current field value to the originally loaded value and clears any validation messages
11204      */
11205     reset : function(){
11206         this.setValue(this.originalValue);
11207         this.validate();
11208     },
11209      /**
11210      * Returns the name of the field
11211      * @return {Mixed} name The name field
11212      */
11213     getName: function(){
11214         return this.name;
11215     },
11216      /**
11217      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11218      * @return {Mixed} value The field value
11219      */
11220     getValue : function(){
11221         
11222         var v = this.inputEl().getValue();
11223         
11224         return v;
11225     },
11226     /**
11227      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11228      * @return {Mixed} value The field value
11229      */
11230     getRawValue : function(){
11231         var v = this.inputEl().getValue();
11232         
11233         return v;
11234     },
11235     
11236     /**
11237      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11238      * @param {Mixed} value The value to set
11239      */
11240     setRawValue : function(v){
11241         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11242     },
11243     
11244     selectText : function(start, end){
11245         var v = this.getRawValue();
11246         if(v.length > 0){
11247             start = start === undefined ? 0 : start;
11248             end = end === undefined ? v.length : end;
11249             var d = this.inputEl().dom;
11250             if(d.setSelectionRange){
11251                 d.setSelectionRange(start, end);
11252             }else if(d.createTextRange){
11253                 var range = d.createTextRange();
11254                 range.moveStart("character", start);
11255                 range.moveEnd("character", v.length-end);
11256                 range.select();
11257             }
11258         }
11259     },
11260     
11261     /**
11262      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11263      * @param {Mixed} value The value to set
11264      */
11265     setValue : function(v){
11266         this.value = v;
11267         if(this.rendered){
11268             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11269             this.validate();
11270         }
11271     },
11272     
11273     /*
11274     processValue : function(value){
11275         if(this.stripCharsRe){
11276             var newValue = value.replace(this.stripCharsRe, '');
11277             if(newValue !== value){
11278                 this.setRawValue(newValue);
11279                 return newValue;
11280             }
11281         }
11282         return value;
11283     },
11284   */
11285     preFocus : function(){
11286         
11287         if(this.selectOnFocus){
11288             this.inputEl().dom.select();
11289         }
11290     },
11291     filterKeys : function(e){
11292         var k = e.getKey();
11293         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11294             return;
11295         }
11296         var c = e.getCharCode(), cc = String.fromCharCode(c);
11297         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11298             return;
11299         }
11300         if(!this.maskRe.test(cc)){
11301             e.stopEvent();
11302         }
11303     },
11304      /**
11305      * Clear any invalid styles/messages for this field
11306      */
11307     clearInvalid : function(){
11308         
11309         if(!this.el || this.preventMark){ // not rendered
11310             return;
11311         }
11312         
11313         
11314         this.el.removeClass([this.invalidClass, 'is-invalid']);
11315         
11316         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11317             
11318             var feedback = this.el.select('.form-control-feedback', true).first();
11319             
11320             if(feedback){
11321                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11322             }
11323             
11324         }
11325         
11326         if(this.indicator){
11327             this.indicator.removeClass('visible');
11328             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11329         }
11330         
11331         this.fireEvent('valid', this);
11332     },
11333     
11334      /**
11335      * Mark this field as valid
11336      */
11337     markValid : function()
11338     {
11339         if(!this.el  || this.preventMark){ // not rendered...
11340             return;
11341         }
11342         
11343         this.el.removeClass([this.invalidClass, this.validClass]);
11344         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11345
11346         var feedback = this.el.select('.form-control-feedback', true).first();
11347             
11348         if(feedback){
11349             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11350         }
11351         
11352         if(this.indicator){
11353             this.indicator.removeClass('visible');
11354             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11355         }
11356         
11357         if(this.disabled){
11358             return;
11359         }
11360         
11361            
11362         if(this.allowBlank && !this.getRawValue().length){
11363             return;
11364         }
11365         if (Roo.bootstrap.version == 3) {
11366             this.el.addClass(this.validClass);
11367         } else {
11368             this.inputEl().addClass('is-valid');
11369         }
11370
11371         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11372             
11373             var feedback = this.el.select('.form-control-feedback', true).first();
11374             
11375             if(feedback){
11376                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11377                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11378             }
11379             
11380         }
11381         
11382         this.fireEvent('valid', this);
11383     },
11384     
11385      /**
11386      * Mark this field as invalid
11387      * @param {String} msg The validation message
11388      */
11389     markInvalid : function(msg)
11390     {
11391         if(!this.el  || this.preventMark){ // not rendered
11392             return;
11393         }
11394         
11395         this.el.removeClass([this.invalidClass, this.validClass]);
11396         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11397         
11398         var feedback = this.el.select('.form-control-feedback', true).first();
11399             
11400         if(feedback){
11401             this.el.select('.form-control-feedback', true).first().removeClass(
11402                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11403         }
11404
11405         if(this.disabled){
11406             return;
11407         }
11408         
11409         if(this.allowBlank && !this.getRawValue().length){
11410             return;
11411         }
11412         
11413         if(this.indicator){
11414             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11415             this.indicator.addClass('visible');
11416         }
11417         if (Roo.bootstrap.version == 3) {
11418             this.el.addClass(this.invalidClass);
11419         } else {
11420             this.inputEl().addClass('is-invalid');
11421         }
11422         
11423         
11424         
11425         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11426             
11427             var feedback = this.el.select('.form-control-feedback', true).first();
11428             
11429             if(feedback){
11430                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11431                 
11432                 if(this.getValue().length || this.forceFeedback){
11433                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11434                 }
11435                 
11436             }
11437             
11438         }
11439         
11440         this.fireEvent('invalid', this, msg);
11441     },
11442     // private
11443     SafariOnKeyDown : function(event)
11444     {
11445         // this is a workaround for a password hang bug on chrome/ webkit.
11446         if (this.inputEl().dom.type != 'password') {
11447             return;
11448         }
11449         
11450         var isSelectAll = false;
11451         
11452         if(this.inputEl().dom.selectionEnd > 0){
11453             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11454         }
11455         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11456             event.preventDefault();
11457             this.setValue('');
11458             return;
11459         }
11460         
11461         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11462             
11463             event.preventDefault();
11464             // this is very hacky as keydown always get's upper case.
11465             //
11466             var cc = String.fromCharCode(event.getCharCode());
11467             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11468             
11469         }
11470     },
11471     adjustWidth : function(tag, w){
11472         tag = tag.toLowerCase();
11473         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11474             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11475                 if(tag == 'input'){
11476                     return w + 2;
11477                 }
11478                 if(tag == 'textarea'){
11479                     return w-2;
11480                 }
11481             }else if(Roo.isOpera){
11482                 if(tag == 'input'){
11483                     return w + 2;
11484                 }
11485                 if(tag == 'textarea'){
11486                     return w-2;
11487                 }
11488             }
11489         }
11490         return w;
11491     },
11492     
11493     setFieldLabel : function(v)
11494     {
11495         if(!this.rendered){
11496             return;
11497         }
11498         
11499         if(this.indicatorEl()){
11500             var ar = this.el.select('label > span',true);
11501             
11502             if (ar.elements.length) {
11503                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11504                 this.fieldLabel = v;
11505                 return;
11506             }
11507             
11508             var br = this.el.select('label',true);
11509             
11510             if(br.elements.length) {
11511                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11512                 this.fieldLabel = v;
11513                 return;
11514             }
11515             
11516             Roo.log('Cannot Found any of label > span || label in input');
11517             return;
11518         }
11519         
11520         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11521         this.fieldLabel = v;
11522         
11523         
11524     }
11525 });
11526
11527  
11528 /*
11529  * - LGPL
11530  *
11531  * Input
11532  * 
11533  */
11534
11535 /**
11536  * @class Roo.bootstrap.TextArea
11537  * @extends Roo.bootstrap.Input
11538  * Bootstrap TextArea class
11539  * @cfg {Number} cols Specifies the visible width of a text area
11540  * @cfg {Number} rows Specifies the visible number of lines in a text area
11541  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11542  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11543  * @cfg {string} html text
11544  * 
11545  * @constructor
11546  * Create a new TextArea
11547  * @param {Object} config The config object
11548  */
11549
11550 Roo.bootstrap.TextArea = function(config){
11551     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11552    
11553 };
11554
11555 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11556      
11557     cols : false,
11558     rows : 5,
11559     readOnly : false,
11560     warp : 'soft',
11561     resize : false,
11562     value: false,
11563     html: false,
11564     
11565     getAutoCreate : function(){
11566         
11567         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11568         
11569         var id = Roo.id();
11570         
11571         var cfg = {};
11572         
11573         if(this.inputType != 'hidden'){
11574             cfg.cls = 'form-group' //input-group
11575         }
11576         
11577         var input =  {
11578             tag: 'textarea',
11579             id : id,
11580             warp : this.warp,
11581             rows : this.rows,
11582             value : this.value || '',
11583             html: this.html || '',
11584             cls : 'form-control',
11585             placeholder : this.placeholder || '' 
11586             
11587         };
11588         
11589         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11590             input.maxLength = this.maxLength;
11591         }
11592         
11593         if(this.resize){
11594             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11595         }
11596         
11597         if(this.cols){
11598             input.cols = this.cols;
11599         }
11600         
11601         if (this.readOnly) {
11602             input.readonly = true;
11603         }
11604         
11605         if (this.name) {
11606             input.name = this.name;
11607         }
11608         
11609         if (this.size) {
11610             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11611         }
11612         
11613         var settings=this;
11614         ['xs','sm','md','lg'].map(function(size){
11615             if (settings[size]) {
11616                 cfg.cls += ' col-' + size + '-' + settings[size];
11617             }
11618         });
11619         
11620         var inputblock = input;
11621         
11622         if(this.hasFeedback && !this.allowBlank){
11623             
11624             var feedback = {
11625                 tag: 'span',
11626                 cls: 'glyphicon form-control-feedback'
11627             };
11628
11629             inputblock = {
11630                 cls : 'has-feedback',
11631                 cn :  [
11632                     input,
11633                     feedback
11634                 ] 
11635             };  
11636         }
11637         
11638         
11639         if (this.before || this.after) {
11640             
11641             inputblock = {
11642                 cls : 'input-group',
11643                 cn :  [] 
11644             };
11645             if (this.before) {
11646                 inputblock.cn.push({
11647                     tag :'span',
11648                     cls : 'input-group-addon',
11649                     html : this.before
11650                 });
11651             }
11652             
11653             inputblock.cn.push(input);
11654             
11655             if(this.hasFeedback && !this.allowBlank){
11656                 inputblock.cls += ' has-feedback';
11657                 inputblock.cn.push(feedback);
11658             }
11659             
11660             if (this.after) {
11661                 inputblock.cn.push({
11662                     tag :'span',
11663                     cls : 'input-group-addon',
11664                     html : this.after
11665                 });
11666             }
11667             
11668         }
11669         
11670         if (align ==='left' && this.fieldLabel.length) {
11671             cfg.cn = [
11672                 {
11673                     tag: 'label',
11674                     'for' :  id,
11675                     cls : 'control-label',
11676                     html : this.fieldLabel
11677                 },
11678                 {
11679                     cls : "",
11680                     cn: [
11681                         inputblock
11682                     ]
11683                 }
11684
11685             ];
11686             
11687             if(this.labelWidth > 12){
11688                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11689             }
11690
11691             if(this.labelWidth < 13 && this.labelmd == 0){
11692                 this.labelmd = this.labelWidth;
11693             }
11694
11695             if(this.labellg > 0){
11696                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11697                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11698             }
11699
11700             if(this.labelmd > 0){
11701                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11702                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11703             }
11704
11705             if(this.labelsm > 0){
11706                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11707                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11708             }
11709
11710             if(this.labelxs > 0){
11711                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11712                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11713             }
11714             
11715         } else if ( this.fieldLabel.length) {
11716             cfg.cn = [
11717
11718                {
11719                    tag: 'label',
11720                    //cls : 'input-group-addon',
11721                    html : this.fieldLabel
11722
11723                },
11724
11725                inputblock
11726
11727            ];
11728
11729         } else {
11730
11731             cfg.cn = [
11732
11733                 inputblock
11734
11735             ];
11736                 
11737         }
11738         
11739         if (this.disabled) {
11740             input.disabled=true;
11741         }
11742         
11743         return cfg;
11744         
11745     },
11746     /**
11747      * return the real textarea element.
11748      */
11749     inputEl: function ()
11750     {
11751         return this.el.select('textarea.form-control',true).first();
11752     },
11753     
11754     /**
11755      * Clear any invalid styles/messages for this field
11756      */
11757     clearInvalid : function()
11758     {
11759         
11760         if(!this.el || this.preventMark){ // not rendered
11761             return;
11762         }
11763         
11764         var label = this.el.select('label', true).first();
11765         var icon = this.el.select('i.fa-star', true).first();
11766         
11767         if(label && icon){
11768             icon.remove();
11769         }
11770         this.el.removeClass( this.validClass);
11771         this.inputEl().removeClass('is-invalid');
11772          
11773         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11774             
11775             var feedback = this.el.select('.form-control-feedback', true).first();
11776             
11777             if(feedback){
11778                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11779             }
11780             
11781         }
11782         
11783         this.fireEvent('valid', this);
11784     },
11785     
11786      /**
11787      * Mark this field as valid
11788      */
11789     markValid : function()
11790     {
11791         if(!this.el  || this.preventMark){ // not rendered
11792             return;
11793         }
11794         
11795         this.el.removeClass([this.invalidClass, this.validClass]);
11796         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11797         
11798         var feedback = this.el.select('.form-control-feedback', true).first();
11799             
11800         if(feedback){
11801             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11802         }
11803
11804         if(this.disabled || this.allowBlank){
11805             return;
11806         }
11807         
11808         var label = this.el.select('label', true).first();
11809         var icon = this.el.select('i.fa-star', true).first();
11810         
11811         if(label && icon){
11812             icon.remove();
11813         }
11814         if (Roo.bootstrap.version == 3) {
11815             this.el.addClass(this.validClass);
11816         } else {
11817             this.inputEl().addClass('is-valid');
11818         }
11819         
11820         
11821         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11822             
11823             var feedback = this.el.select('.form-control-feedback', true).first();
11824             
11825             if(feedback){
11826                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11827                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11828             }
11829             
11830         }
11831         
11832         this.fireEvent('valid', this);
11833     },
11834     
11835      /**
11836      * Mark this field as invalid
11837      * @param {String} msg The validation message
11838      */
11839     markInvalid : function(msg)
11840     {
11841         if(!this.el  || this.preventMark){ // not rendered
11842             return;
11843         }
11844         
11845         this.el.removeClass([this.invalidClass, this.validClass]);
11846         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11847         
11848         var feedback = this.el.select('.form-control-feedback', true).first();
11849             
11850         if(feedback){
11851             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11852         }
11853
11854         if(this.disabled || this.allowBlank){
11855             return;
11856         }
11857         
11858         var label = this.el.select('label', true).first();
11859         var icon = this.el.select('i.fa-star', true).first();
11860         
11861         if(!this.getValue().length && label && !icon){
11862             this.el.createChild({
11863                 tag : 'i',
11864                 cls : 'text-danger fa fa-lg fa-star',
11865                 tooltip : 'This field is required',
11866                 style : 'margin-right:5px;'
11867             }, label, true);
11868         }
11869         
11870         if (Roo.bootstrap.version == 3) {
11871             this.el.addClass(this.invalidClass);
11872         } else {
11873             this.inputEl().addClass('is-invalid');
11874         }
11875         
11876         // fixme ... this may be depricated need to test..
11877         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11878             
11879             var feedback = this.el.select('.form-control-feedback', true).first();
11880             
11881             if(feedback){
11882                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11883                 
11884                 if(this.getValue().length || this.forceFeedback){
11885                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11886                 }
11887                 
11888             }
11889             
11890         }
11891         
11892         this.fireEvent('invalid', this, msg);
11893     }
11894 });
11895
11896  
11897 /*
11898  * - LGPL
11899  *
11900  * trigger field - base class for combo..
11901  * 
11902  */
11903  
11904 /**
11905  * @class Roo.bootstrap.TriggerField
11906  * @extends Roo.bootstrap.Input
11907  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11908  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11909  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11910  * for which you can provide a custom implementation.  For example:
11911  * <pre><code>
11912 var trigger = new Roo.bootstrap.TriggerField();
11913 trigger.onTriggerClick = myTriggerFn;
11914 trigger.applyTo('my-field');
11915 </code></pre>
11916  *
11917  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11918  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11919  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11920  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11921  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11922
11923  * @constructor
11924  * Create a new TriggerField.
11925  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11926  * to the base TextField)
11927  */
11928 Roo.bootstrap.TriggerField = function(config){
11929     this.mimicing = false;
11930     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11931 };
11932
11933 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11934     /**
11935      * @cfg {String} triggerClass A CSS class to apply to the trigger
11936      */
11937      /**
11938      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11939      */
11940     hideTrigger:false,
11941
11942     /**
11943      * @cfg {Boolean} removable (true|false) special filter default false
11944      */
11945     removable : false,
11946     
11947     /** @cfg {Boolean} grow @hide */
11948     /** @cfg {Number} growMin @hide */
11949     /** @cfg {Number} growMax @hide */
11950
11951     /**
11952      * @hide 
11953      * @method
11954      */
11955     autoSize: Roo.emptyFn,
11956     // private
11957     monitorTab : true,
11958     // private
11959     deferHeight : true,
11960
11961     
11962     actionMode : 'wrap',
11963     
11964     caret : false,
11965     
11966     
11967     getAutoCreate : function(){
11968        
11969         var align = this.labelAlign || this.parentLabelAlign();
11970         
11971         var id = Roo.id();
11972         
11973         var cfg = {
11974             cls: 'form-group' //input-group
11975         };
11976         
11977         
11978         var input =  {
11979             tag: 'input',
11980             id : id,
11981             type : this.inputType,
11982             cls : 'form-control',
11983             autocomplete: 'new-password',
11984             placeholder : this.placeholder || '' 
11985             
11986         };
11987         if (this.name) {
11988             input.name = this.name;
11989         }
11990         if (this.size) {
11991             input.cls += ' input-' + this.size;
11992         }
11993         
11994         if (this.disabled) {
11995             input.disabled=true;
11996         }
11997         
11998         var inputblock = input;
11999         
12000         if(this.hasFeedback && !this.allowBlank){
12001             
12002             var feedback = {
12003                 tag: 'span',
12004                 cls: 'glyphicon form-control-feedback'
12005             };
12006             
12007             if(this.removable && !this.editable  ){
12008                 inputblock = {
12009                     cls : 'has-feedback',
12010                     cn :  [
12011                         inputblock,
12012                         {
12013                             tag: 'button',
12014                             html : 'x',
12015                             cls : 'roo-combo-removable-btn close'
12016                         },
12017                         feedback
12018                     ] 
12019                 };
12020             } else {
12021                 inputblock = {
12022                     cls : 'has-feedback',
12023                     cn :  [
12024                         inputblock,
12025                         feedback
12026                     ] 
12027                 };
12028             }
12029
12030         } else {
12031             if(this.removable && !this.editable ){
12032                 inputblock = {
12033                     cls : 'roo-removable',
12034                     cn :  [
12035                         inputblock,
12036                         {
12037                             tag: 'button',
12038                             html : 'x',
12039                             cls : 'roo-combo-removable-btn close'
12040                         }
12041                     ] 
12042                 };
12043             }
12044         }
12045         
12046         if (this.before || this.after) {
12047             
12048             inputblock = {
12049                 cls : 'input-group',
12050                 cn :  [] 
12051             };
12052             if (this.before) {
12053                 inputblock.cn.push({
12054                     tag :'span',
12055                     cls : 'input-group-addon input-group-prepend input-group-text',
12056                     html : this.before
12057                 });
12058             }
12059             
12060             inputblock.cn.push(input);
12061             
12062             if(this.hasFeedback && !this.allowBlank){
12063                 inputblock.cls += ' has-feedback';
12064                 inputblock.cn.push(feedback);
12065             }
12066             
12067             if (this.after) {
12068                 inputblock.cn.push({
12069                     tag :'span',
12070                     cls : 'input-group-addon input-group-append input-group-text',
12071                     html : this.after
12072                 });
12073             }
12074             
12075         };
12076         
12077       
12078         
12079         var ibwrap = inputblock;
12080         
12081         if(this.multiple){
12082             ibwrap = {
12083                 tag: 'ul',
12084                 cls: 'roo-select2-choices',
12085                 cn:[
12086                     {
12087                         tag: 'li',
12088                         cls: 'roo-select2-search-field',
12089                         cn: [
12090
12091                             inputblock
12092                         ]
12093                     }
12094                 ]
12095             };
12096                 
12097         }
12098         
12099         var combobox = {
12100             cls: 'roo-select2-container input-group',
12101             cn: [
12102                  {
12103                     tag: 'input',
12104                     type : 'hidden',
12105                     cls: 'form-hidden-field'
12106                 },
12107                 ibwrap
12108             ]
12109         };
12110         
12111         if(!this.multiple && this.showToggleBtn){
12112             
12113             var caret = {
12114                         tag: 'span',
12115                         cls: 'caret'
12116              };
12117             if (this.caret != false) {
12118                 caret = {
12119                      tag: 'i',
12120                      cls: 'fa fa-' + this.caret
12121                 };
12122                 
12123             }
12124             
12125             combobox.cn.push({
12126                 tag :'span',
12127                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12128                 cn : [
12129                     Roo.bootstrap.version == 3 ? caret : '',
12130                     {
12131                         tag: 'span',
12132                         cls: 'combobox-clear',
12133                         cn  : [
12134                             {
12135                                 tag : 'i',
12136                                 cls: 'icon-remove'
12137                             }
12138                         ]
12139                     }
12140                 ]
12141
12142             })
12143         }
12144         
12145         if(this.multiple){
12146             combobox.cls += ' roo-select2-container-multi';
12147         }
12148          var indicator = {
12149             tag : 'i',
12150             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12151             tooltip : 'This field is required'
12152         };
12153         if (Roo.bootstrap.version == 4) {
12154             indicator = {
12155                 tag : 'i',
12156                 style : 'display:none'
12157             };
12158         }
12159         
12160         
12161         if (align ==='left' && this.fieldLabel.length) {
12162             
12163             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12164
12165             cfg.cn = [
12166                 indicator,
12167                 {
12168                     tag: 'label',
12169                     'for' :  id,
12170                     cls : 'control-label',
12171                     html : this.fieldLabel
12172
12173                 },
12174                 {
12175                     cls : "", 
12176                     cn: [
12177                         combobox
12178                     ]
12179                 }
12180
12181             ];
12182             
12183             var labelCfg = cfg.cn[1];
12184             var contentCfg = cfg.cn[2];
12185             
12186             if(this.indicatorpos == 'right'){
12187                 cfg.cn = [
12188                     {
12189                         tag: 'label',
12190                         'for' :  id,
12191                         cls : 'control-label',
12192                         cn : [
12193                             {
12194                                 tag : 'span',
12195                                 html : this.fieldLabel
12196                             },
12197                             indicator
12198                         ]
12199                     },
12200                     {
12201                         cls : "", 
12202                         cn: [
12203                             combobox
12204                         ]
12205                     }
12206
12207                 ];
12208                 
12209                 labelCfg = cfg.cn[0];
12210                 contentCfg = cfg.cn[1];
12211             }
12212             
12213             if(this.labelWidth > 12){
12214                 labelCfg.style = "width: " + this.labelWidth + 'px';
12215             }
12216             
12217             if(this.labelWidth < 13 && this.labelmd == 0){
12218                 this.labelmd = this.labelWidth;
12219             }
12220             
12221             if(this.labellg > 0){
12222                 labelCfg.cls += ' col-lg-' + this.labellg;
12223                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12224             }
12225             
12226             if(this.labelmd > 0){
12227                 labelCfg.cls += ' col-md-' + this.labelmd;
12228                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12229             }
12230             
12231             if(this.labelsm > 0){
12232                 labelCfg.cls += ' col-sm-' + this.labelsm;
12233                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12234             }
12235             
12236             if(this.labelxs > 0){
12237                 labelCfg.cls += ' col-xs-' + this.labelxs;
12238                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12239             }
12240             
12241         } else if ( this.fieldLabel.length) {
12242 //                Roo.log(" label");
12243             cfg.cn = [
12244                 indicator,
12245                {
12246                    tag: 'label',
12247                    //cls : 'input-group-addon',
12248                    html : this.fieldLabel
12249
12250                },
12251
12252                combobox
12253
12254             ];
12255             
12256             if(this.indicatorpos == 'right'){
12257                 
12258                 cfg.cn = [
12259                     {
12260                        tag: 'label',
12261                        cn : [
12262                            {
12263                                tag : 'span',
12264                                html : this.fieldLabel
12265                            },
12266                            indicator
12267                        ]
12268
12269                     },
12270                     combobox
12271
12272                 ];
12273
12274             }
12275
12276         } else {
12277             
12278 //                Roo.log(" no label && no align");
12279                 cfg = combobox
12280                      
12281                 
12282         }
12283         
12284         var settings=this;
12285         ['xs','sm','md','lg'].map(function(size){
12286             if (settings[size]) {
12287                 cfg.cls += ' col-' + size + '-' + settings[size];
12288             }
12289         });
12290         
12291         return cfg;
12292         
12293     },
12294     
12295     
12296     
12297     // private
12298     onResize : function(w, h){
12299 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12300 //        if(typeof w == 'number'){
12301 //            var x = w - this.trigger.getWidth();
12302 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12303 //            this.trigger.setStyle('left', x+'px');
12304 //        }
12305     },
12306
12307     // private
12308     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12309
12310     // private
12311     getResizeEl : function(){
12312         return this.inputEl();
12313     },
12314
12315     // private
12316     getPositionEl : function(){
12317         return this.inputEl();
12318     },
12319
12320     // private
12321     alignErrorIcon : function(){
12322         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12323     },
12324
12325     // private
12326     initEvents : function(){
12327         
12328         this.createList();
12329         
12330         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12331         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12332         if(!this.multiple && this.showToggleBtn){
12333             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12334             if(this.hideTrigger){
12335                 this.trigger.setDisplayed(false);
12336             }
12337             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12338         }
12339         
12340         if(this.multiple){
12341             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12342         }
12343         
12344         if(this.removable && !this.editable && !this.tickable){
12345             var close = this.closeTriggerEl();
12346             
12347             if(close){
12348                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12349                 close.on('click', this.removeBtnClick, this, close);
12350             }
12351         }
12352         
12353         //this.trigger.addClassOnOver('x-form-trigger-over');
12354         //this.trigger.addClassOnClick('x-form-trigger-click');
12355         
12356         //if(!this.width){
12357         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12358         //}
12359     },
12360     
12361     closeTriggerEl : function()
12362     {
12363         var close = this.el.select('.roo-combo-removable-btn', true).first();
12364         return close ? close : false;
12365     },
12366     
12367     removeBtnClick : function(e, h, el)
12368     {
12369         e.preventDefault();
12370         
12371         if(this.fireEvent("remove", this) !== false){
12372             this.reset();
12373             this.fireEvent("afterremove", this)
12374         }
12375     },
12376     
12377     createList : function()
12378     {
12379         this.list = Roo.get(document.body).createChild({
12380             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12381             cls: 'typeahead typeahead-long dropdown-menu shadow',
12382             style: 'display:none'
12383         });
12384         
12385         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12386         
12387     },
12388
12389     // private
12390     initTrigger : function(){
12391        
12392     },
12393
12394     // private
12395     onDestroy : function(){
12396         if(this.trigger){
12397             this.trigger.removeAllListeners();
12398           //  this.trigger.remove();
12399         }
12400         //if(this.wrap){
12401         //    this.wrap.remove();
12402         //}
12403         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12404     },
12405
12406     // private
12407     onFocus : function(){
12408         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12409         /*
12410         if(!this.mimicing){
12411             this.wrap.addClass('x-trigger-wrap-focus');
12412             this.mimicing = true;
12413             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12414             if(this.monitorTab){
12415                 this.el.on("keydown", this.checkTab, this);
12416             }
12417         }
12418         */
12419     },
12420
12421     // private
12422     checkTab : function(e){
12423         if(e.getKey() == e.TAB){
12424             this.triggerBlur();
12425         }
12426     },
12427
12428     // private
12429     onBlur : function(){
12430         // do nothing
12431     },
12432
12433     // private
12434     mimicBlur : function(e, t){
12435         /*
12436         if(!this.wrap.contains(t) && this.validateBlur()){
12437             this.triggerBlur();
12438         }
12439         */
12440     },
12441
12442     // private
12443     triggerBlur : function(){
12444         this.mimicing = false;
12445         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12446         if(this.monitorTab){
12447             this.el.un("keydown", this.checkTab, this);
12448         }
12449         //this.wrap.removeClass('x-trigger-wrap-focus');
12450         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12451     },
12452
12453     // private
12454     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12455     validateBlur : function(e, t){
12456         return true;
12457     },
12458
12459     // private
12460     onDisable : function(){
12461         this.inputEl().dom.disabled = true;
12462         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12463         //if(this.wrap){
12464         //    this.wrap.addClass('x-item-disabled');
12465         //}
12466     },
12467
12468     // private
12469     onEnable : function(){
12470         this.inputEl().dom.disabled = false;
12471         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12472         //if(this.wrap){
12473         //    this.el.removeClass('x-item-disabled');
12474         //}
12475     },
12476
12477     // private
12478     onShow : function(){
12479         var ae = this.getActionEl();
12480         
12481         if(ae){
12482             ae.dom.style.display = '';
12483             ae.dom.style.visibility = 'visible';
12484         }
12485     },
12486
12487     // private
12488     
12489     onHide : function(){
12490         var ae = this.getActionEl();
12491         ae.dom.style.display = 'none';
12492     },
12493
12494     /**
12495      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12496      * by an implementing function.
12497      * @method
12498      * @param {EventObject} e
12499      */
12500     onTriggerClick : Roo.emptyFn
12501 });
12502  
12503 /*
12504 * Licence: LGPL
12505 */
12506
12507 /**
12508  * @class Roo.bootstrap.CardUploader
12509  * @extends Roo.bootstrap.Button
12510  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12511  * @cfg {Number} errorTimeout default 3000
12512  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12513  * @cfg {Array}  html The button text.
12514
12515  *
12516  * @constructor
12517  * Create a new CardUploader
12518  * @param {Object} config The config object
12519  */
12520
12521 Roo.bootstrap.CardUploader = function(config){
12522     
12523  
12524     
12525     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12526     
12527     
12528     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12529         return r.data.id
12530      });
12531     
12532      this.addEvents({
12533          // raw events
12534         /**
12535          * @event preview
12536          * When a image is clicked on - and needs to display a slideshow or similar..
12537          * @param {Roo.bootstrap.Card} this
12538          * @param {Object} The image information data 
12539          *
12540          */
12541         'preview' : true,
12542          /**
12543          * @event download
12544          * When a the download link is clicked
12545          * @param {Roo.bootstrap.Card} this
12546          * @param {Object} The image information data  contains 
12547          */
12548         'download' : true
12549         
12550     });
12551 };
12552  
12553 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12554     
12555      
12556     errorTimeout : 3000,
12557      
12558     images : false,
12559    
12560     fileCollection : false,
12561     allowBlank : true,
12562     
12563     getAutoCreate : function()
12564     {
12565         
12566         var cfg =  {
12567             cls :'form-group' ,
12568             cn : [
12569                
12570                 {
12571                     tag: 'label',
12572                    //cls : 'input-group-addon',
12573                     html : this.fieldLabel
12574
12575                 },
12576
12577                 {
12578                     tag: 'input',
12579                     type : 'hidden',
12580                     name : this.name,
12581                     value : this.value,
12582                     cls : 'd-none  form-control'
12583                 },
12584                 
12585                 {
12586                     tag: 'input',
12587                     multiple : 'multiple',
12588                     type : 'file',
12589                     cls : 'd-none  roo-card-upload-selector'
12590                 },
12591                 
12592                 {
12593                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12594                 },
12595                 {
12596                     cls : 'card-columns roo-card-uploader-container'
12597                 }
12598
12599             ]
12600         };
12601            
12602          
12603         return cfg;
12604     },
12605     
12606     getChildContainer : function() /// what children are added to.
12607     {
12608         return this.containerEl;
12609     },
12610    
12611     getButtonContainer : function() /// what children are added to.
12612     {
12613         return this.el.select(".roo-card-uploader-button-container").first();
12614     },
12615    
12616     initEvents : function()
12617     {
12618         
12619         Roo.bootstrap.Input.prototype.initEvents.call(this);
12620         
12621         var t = this;
12622         this.addxtype({
12623             xns: Roo.bootstrap,
12624
12625             xtype : 'Button',
12626             container_method : 'getButtonContainer' ,            
12627             html :  this.html, // fix changable?
12628             cls : 'w-100 ',
12629             listeners : {
12630                 'click' : function(btn, e) {
12631                     t.onClick(e);
12632                 }
12633             }
12634         });
12635         
12636         
12637         
12638         
12639         this.urlAPI = (window.createObjectURL && window) || 
12640                                 (window.URL && URL.revokeObjectURL && URL) || 
12641                                 (window.webkitURL && webkitURL);
12642                         
12643          
12644          
12645          
12646         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12647         
12648         this.selectorEl.on('change', this.onFileSelected, this);
12649         if (this.images) {
12650             var t = this;
12651             this.images.forEach(function(img) {
12652                 t.addCard(img)
12653             });
12654             this.images = false;
12655         }
12656         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12657          
12658        
12659     },
12660     
12661    
12662     onClick : function(e)
12663     {
12664         e.preventDefault();
12665          
12666         this.selectorEl.dom.click();
12667          
12668     },
12669     
12670     onFileSelected : function(e)
12671     {
12672         e.preventDefault();
12673         
12674         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12675             return;
12676         }
12677         
12678         Roo.each(this.selectorEl.dom.files, function(file){    
12679             this.addFile(file);
12680         }, this);
12681          
12682     },
12683     
12684       
12685     
12686       
12687     
12688     addFile : function(file)
12689     {
12690            
12691         if(typeof(file) === 'string'){
12692             throw "Add file by name?"; // should not happen
12693             return;
12694         }
12695         
12696         if(!file || !this.urlAPI){
12697             return;
12698         }
12699         
12700         // file;
12701         // file.type;
12702         
12703         var _this = this;
12704         
12705         
12706         var url = _this.urlAPI.createObjectURL( file);
12707            
12708         this.addCard({
12709             id : Roo.bootstrap.CardUploader.ID--,
12710             is_uploaded : false,
12711             src : url,
12712             srcfile : file,
12713             title : file.name,
12714             mimetype : file.type,
12715             preview : false,
12716             is_deleted : 0
12717         });
12718         
12719     },
12720     
12721     /**
12722      * addCard - add an Attachment to the uploader
12723      * @param data - the data about the image to upload
12724      *
12725      * {
12726           id : 123
12727           title : "Title of file",
12728           is_uploaded : false,
12729           src : "http://.....",
12730           srcfile : { the File upload object },
12731           mimetype : file.type,
12732           preview : false,
12733           is_deleted : 0
12734           .. any other data...
12735         }
12736      *
12737      * 
12738     */
12739     
12740     addCard : function (data)
12741     {
12742         // hidden input element?
12743         // if the file is not an image...
12744         //then we need to use something other that and header_image
12745         var t = this;
12746         //   remove.....
12747         var footer = [
12748             {
12749                 xns : Roo.bootstrap,
12750                 xtype : 'CardFooter',
12751                  items: [
12752                     {
12753                         xns : Roo.bootstrap,
12754                         xtype : 'Element',
12755                         cls : 'd-flex',
12756                         items : [
12757                             
12758                             {
12759                                 xns : Roo.bootstrap,
12760                                 xtype : 'Button',
12761                                 html : String.format("<small>{0}</small>", data.title),
12762                                 cls : 'col-10 text-left',
12763                                 size: 'sm',
12764                                 weight: 'link',
12765                                 fa : 'download',
12766                                 listeners : {
12767                                     click : function() {
12768                                      
12769                                         t.fireEvent( "download", t, data );
12770                                     }
12771                                 }
12772                             },
12773                           
12774                             {
12775                                 xns : Roo.bootstrap,
12776                                 xtype : 'Button',
12777                                 style: 'max-height: 28px; ',
12778                                 size : 'sm',
12779                                 weight: 'danger',
12780                                 cls : 'col-2',
12781                                 fa : 'times',
12782                                 listeners : {
12783                                     click : function() {
12784                                         t.removeCard(data.id)
12785                                     }
12786                                 }
12787                             }
12788                         ]
12789                     }
12790                     
12791                 ] 
12792             }
12793             
12794         ];
12795         
12796         var cn = this.addxtype(
12797             {
12798                  
12799                 xns : Roo.bootstrap,
12800                 xtype : 'Card',
12801                 closeable : true,
12802                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12803                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12804                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12805                 data : data,
12806                 html : false,
12807                  
12808                 items : footer,
12809                 initEvents : function() {
12810                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12811                     var card = this;
12812                     this.imgEl = this.el.select('.card-img-top').first();
12813                     if (this.imgEl) {
12814                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12815                         this.imgEl.set({ 'pointer' : 'cursor' });
12816                                   
12817                     }
12818                     this.getCardFooter().addClass('p-1');
12819                     
12820                   
12821                 }
12822                 
12823             }
12824         );
12825         // dont' really need ot update items.
12826         // this.items.push(cn);
12827         this.fileCollection.add(cn);
12828         
12829         if (!data.srcfile) {
12830             this.updateInput();
12831             return;
12832         }
12833             
12834         var _t = this;
12835         var reader = new FileReader();
12836         reader.addEventListener("load", function() {  
12837             data.srcdata =  reader.result;
12838             _t.updateInput();
12839         });
12840         reader.readAsDataURL(data.srcfile);
12841         
12842         
12843         
12844     },
12845     removeCard : function(id)
12846     {
12847         
12848         var card  = this.fileCollection.get(id);
12849         card.data.is_deleted = 1;
12850         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12851         //this.fileCollection.remove(card);
12852         //this.items = this.items.filter(function(e) { return e != card });
12853         // dont' really need ot update items.
12854         card.el.dom.parentNode.removeChild(card.el.dom);
12855         this.updateInput();
12856
12857         
12858     },
12859     reset: function()
12860     {
12861         this.fileCollection.each(function(card) {
12862             if (card.el.dom && card.el.dom.parentNode) {
12863                 card.el.dom.parentNode.removeChild(card.el.dom);
12864             }
12865         });
12866         this.fileCollection.clear();
12867         this.updateInput();
12868     },
12869     
12870     updateInput : function()
12871     {
12872          var data = [];
12873         this.fileCollection.each(function(e) {
12874             data.push(e.data);
12875             
12876         });
12877         this.inputEl().dom.value = JSON.stringify(data);
12878         
12879         
12880         
12881     }
12882     
12883     
12884 });
12885
12886
12887 Roo.bootstrap.CardUploader.ID = -1;/*
12888  * Based on:
12889  * Ext JS Library 1.1.1
12890  * Copyright(c) 2006-2007, Ext JS, LLC.
12891  *
12892  * Originally Released Under LGPL - original licence link has changed is not relivant.
12893  *
12894  * Fork - LGPL
12895  * <script type="text/javascript">
12896  */
12897
12898
12899 /**
12900  * @class Roo.data.SortTypes
12901  * @singleton
12902  * Defines the default sorting (casting?) comparison functions used when sorting data.
12903  */
12904 Roo.data.SortTypes = {
12905     /**
12906      * Default sort that does nothing
12907      * @param {Mixed} s The value being converted
12908      * @return {Mixed} The comparison value
12909      */
12910     none : function(s){
12911         return s;
12912     },
12913     
12914     /**
12915      * The regular expression used to strip tags
12916      * @type {RegExp}
12917      * @property
12918      */
12919     stripTagsRE : /<\/?[^>]+>/gi,
12920     
12921     /**
12922      * Strips all HTML tags to sort on text only
12923      * @param {Mixed} s The value being converted
12924      * @return {String} The comparison value
12925      */
12926     asText : function(s){
12927         return String(s).replace(this.stripTagsRE, "");
12928     },
12929     
12930     /**
12931      * Strips all HTML tags to sort on text only - Case insensitive
12932      * @param {Mixed} s The value being converted
12933      * @return {String} The comparison value
12934      */
12935     asUCText : function(s){
12936         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12937     },
12938     
12939     /**
12940      * Case insensitive string
12941      * @param {Mixed} s The value being converted
12942      * @return {String} The comparison value
12943      */
12944     asUCString : function(s) {
12945         return String(s).toUpperCase();
12946     },
12947     
12948     /**
12949      * Date sorting
12950      * @param {Mixed} s The value being converted
12951      * @return {Number} The comparison value
12952      */
12953     asDate : function(s) {
12954         if(!s){
12955             return 0;
12956         }
12957         if(s instanceof Date){
12958             return s.getTime();
12959         }
12960         return Date.parse(String(s));
12961     },
12962     
12963     /**
12964      * Float sorting
12965      * @param {Mixed} s The value being converted
12966      * @return {Float} The comparison value
12967      */
12968     asFloat : function(s) {
12969         var val = parseFloat(String(s).replace(/,/g, ""));
12970         if(isNaN(val)) {
12971             val = 0;
12972         }
12973         return val;
12974     },
12975     
12976     /**
12977      * Integer sorting
12978      * @param {Mixed} s The value being converted
12979      * @return {Number} The comparison value
12980      */
12981     asInt : function(s) {
12982         var val = parseInt(String(s).replace(/,/g, ""));
12983         if(isNaN(val)) {
12984             val = 0;
12985         }
12986         return val;
12987     }
12988 };/*
12989  * Based on:
12990  * Ext JS Library 1.1.1
12991  * Copyright(c) 2006-2007, Ext JS, LLC.
12992  *
12993  * Originally Released Under LGPL - original licence link has changed is not relivant.
12994  *
12995  * Fork - LGPL
12996  * <script type="text/javascript">
12997  */
12998
12999 /**
13000 * @class Roo.data.Record
13001  * Instances of this class encapsulate both record <em>definition</em> information, and record
13002  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13003  * to access Records cached in an {@link Roo.data.Store} object.<br>
13004  * <p>
13005  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13006  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13007  * objects.<br>
13008  * <p>
13009  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13010  * @constructor
13011  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13012  * {@link #create}. The parameters are the same.
13013  * @param {Array} data An associative Array of data values keyed by the field name.
13014  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13015  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13016  * not specified an integer id is generated.
13017  */
13018 Roo.data.Record = function(data, id){
13019     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13020     this.data = data;
13021 };
13022
13023 /**
13024  * Generate a constructor for a specific record layout.
13025  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13026  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13027  * Each field definition object may contain the following properties: <ul>
13028  * <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,
13029  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13030  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13031  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13032  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13033  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13034  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13035  * this may be omitted.</p></li>
13036  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13037  * <ul><li>auto (Default, implies no conversion)</li>
13038  * <li>string</li>
13039  * <li>int</li>
13040  * <li>float</li>
13041  * <li>boolean</li>
13042  * <li>date</li></ul></p></li>
13043  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13044  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13045  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13046  * by the Reader into an object that will be stored in the Record. It is passed the
13047  * following parameters:<ul>
13048  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13049  * </ul></p></li>
13050  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13051  * </ul>
13052  * <br>usage:<br><pre><code>
13053 var TopicRecord = Roo.data.Record.create(
13054     {name: 'title', mapping: 'topic_title'},
13055     {name: 'author', mapping: 'username'},
13056     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13057     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13058     {name: 'lastPoster', mapping: 'user2'},
13059     {name: 'excerpt', mapping: 'post_text'}
13060 );
13061
13062 var myNewRecord = new TopicRecord({
13063     title: 'Do my job please',
13064     author: 'noobie',
13065     totalPosts: 1,
13066     lastPost: new Date(),
13067     lastPoster: 'Animal',
13068     excerpt: 'No way dude!'
13069 });
13070 myStore.add(myNewRecord);
13071 </code></pre>
13072  * @method create
13073  * @static
13074  */
13075 Roo.data.Record.create = function(o){
13076     var f = function(){
13077         f.superclass.constructor.apply(this, arguments);
13078     };
13079     Roo.extend(f, Roo.data.Record);
13080     var p = f.prototype;
13081     p.fields = new Roo.util.MixedCollection(false, function(field){
13082         return field.name;
13083     });
13084     for(var i = 0, len = o.length; i < len; i++){
13085         p.fields.add(new Roo.data.Field(o[i]));
13086     }
13087     f.getField = function(name){
13088         return p.fields.get(name);  
13089     };
13090     return f;
13091 };
13092
13093 Roo.data.Record.AUTO_ID = 1000;
13094 Roo.data.Record.EDIT = 'edit';
13095 Roo.data.Record.REJECT = 'reject';
13096 Roo.data.Record.COMMIT = 'commit';
13097
13098 Roo.data.Record.prototype = {
13099     /**
13100      * Readonly flag - true if this record has been modified.
13101      * @type Boolean
13102      */
13103     dirty : false,
13104     editing : false,
13105     error: null,
13106     modified: null,
13107
13108     // private
13109     join : function(store){
13110         this.store = store;
13111     },
13112
13113     /**
13114      * Set the named field to the specified value.
13115      * @param {String} name The name of the field to set.
13116      * @param {Object} value The value to set the field to.
13117      */
13118     set : function(name, value){
13119         if(this.data[name] == value){
13120             return;
13121         }
13122         this.dirty = true;
13123         if(!this.modified){
13124             this.modified = {};
13125         }
13126         if(typeof this.modified[name] == 'undefined'){
13127             this.modified[name] = this.data[name];
13128         }
13129         this.data[name] = value;
13130         if(!this.editing && this.store){
13131             this.store.afterEdit(this);
13132         }       
13133     },
13134
13135     /**
13136      * Get the value of the named field.
13137      * @param {String} name The name of the field to get the value of.
13138      * @return {Object} The value of the field.
13139      */
13140     get : function(name){
13141         return this.data[name]; 
13142     },
13143
13144     // private
13145     beginEdit : function(){
13146         this.editing = true;
13147         this.modified = {}; 
13148     },
13149
13150     // private
13151     cancelEdit : function(){
13152         this.editing = false;
13153         delete this.modified;
13154     },
13155
13156     // private
13157     endEdit : function(){
13158         this.editing = false;
13159         if(this.dirty && this.store){
13160             this.store.afterEdit(this);
13161         }
13162     },
13163
13164     /**
13165      * Usually called by the {@link Roo.data.Store} which owns the Record.
13166      * Rejects all changes made to the Record since either creation, or the last commit operation.
13167      * Modified fields are reverted to their original values.
13168      * <p>
13169      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13170      * of reject operations.
13171      */
13172     reject : function(){
13173         var m = this.modified;
13174         for(var n in m){
13175             if(typeof m[n] != "function"){
13176                 this.data[n] = m[n];
13177             }
13178         }
13179         this.dirty = false;
13180         delete this.modified;
13181         this.editing = false;
13182         if(this.store){
13183             this.store.afterReject(this);
13184         }
13185     },
13186
13187     /**
13188      * Usually called by the {@link Roo.data.Store} which owns the Record.
13189      * Commits all changes made to the Record since either creation, or the last commit operation.
13190      * <p>
13191      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13192      * of commit operations.
13193      */
13194     commit : function(){
13195         this.dirty = false;
13196         delete this.modified;
13197         this.editing = false;
13198         if(this.store){
13199             this.store.afterCommit(this);
13200         }
13201     },
13202
13203     // private
13204     hasError : function(){
13205         return this.error != null;
13206     },
13207
13208     // private
13209     clearError : function(){
13210         this.error = null;
13211     },
13212
13213     /**
13214      * Creates a copy of this record.
13215      * @param {String} id (optional) A new record id if you don't want to use this record's id
13216      * @return {Record}
13217      */
13218     copy : function(newId) {
13219         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13220     }
13221 };/*
13222  * Based on:
13223  * Ext JS Library 1.1.1
13224  * Copyright(c) 2006-2007, Ext JS, LLC.
13225  *
13226  * Originally Released Under LGPL - original licence link has changed is not relivant.
13227  *
13228  * Fork - LGPL
13229  * <script type="text/javascript">
13230  */
13231
13232
13233
13234 /**
13235  * @class Roo.data.Store
13236  * @extends Roo.util.Observable
13237  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13238  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13239  * <p>
13240  * 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
13241  * has no knowledge of the format of the data returned by the Proxy.<br>
13242  * <p>
13243  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13244  * instances from the data object. These records are cached and made available through accessor functions.
13245  * @constructor
13246  * Creates a new Store.
13247  * @param {Object} config A config object containing the objects needed for the Store to access data,
13248  * and read the data into Records.
13249  */
13250 Roo.data.Store = function(config){
13251     this.data = new Roo.util.MixedCollection(false);
13252     this.data.getKey = function(o){
13253         return o.id;
13254     };
13255     this.baseParams = {};
13256     // private
13257     this.paramNames = {
13258         "start" : "start",
13259         "limit" : "limit",
13260         "sort" : "sort",
13261         "dir" : "dir",
13262         "multisort" : "_multisort"
13263     };
13264
13265     if(config && config.data){
13266         this.inlineData = config.data;
13267         delete config.data;
13268     }
13269
13270     Roo.apply(this, config);
13271     
13272     if(this.reader){ // reader passed
13273         this.reader = Roo.factory(this.reader, Roo.data);
13274         this.reader.xmodule = this.xmodule || false;
13275         if(!this.recordType){
13276             this.recordType = this.reader.recordType;
13277         }
13278         if(this.reader.onMetaChange){
13279             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13280         }
13281     }
13282
13283     if(this.recordType){
13284         this.fields = this.recordType.prototype.fields;
13285     }
13286     this.modified = [];
13287
13288     this.addEvents({
13289         /**
13290          * @event datachanged
13291          * Fires when the data cache has changed, and a widget which is using this Store
13292          * as a Record cache should refresh its view.
13293          * @param {Store} this
13294          */
13295         datachanged : true,
13296         /**
13297          * @event metachange
13298          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13299          * @param {Store} this
13300          * @param {Object} meta The JSON metadata
13301          */
13302         metachange : true,
13303         /**
13304          * @event add
13305          * Fires when Records have been added to the Store
13306          * @param {Store} this
13307          * @param {Roo.data.Record[]} records The array of Records added
13308          * @param {Number} index The index at which the record(s) were added
13309          */
13310         add : true,
13311         /**
13312          * @event remove
13313          * Fires when a Record has been removed from the Store
13314          * @param {Store} this
13315          * @param {Roo.data.Record} record The Record that was removed
13316          * @param {Number} index The index at which the record was removed
13317          */
13318         remove : true,
13319         /**
13320          * @event update
13321          * Fires when a Record has been updated
13322          * @param {Store} this
13323          * @param {Roo.data.Record} record The Record that was updated
13324          * @param {String} operation The update operation being performed.  Value may be one of:
13325          * <pre><code>
13326  Roo.data.Record.EDIT
13327  Roo.data.Record.REJECT
13328  Roo.data.Record.COMMIT
13329          * </code></pre>
13330          */
13331         update : true,
13332         /**
13333          * @event clear
13334          * Fires when the data cache has been cleared.
13335          * @param {Store} this
13336          */
13337         clear : true,
13338         /**
13339          * @event beforeload
13340          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13341          * the load action will be canceled.
13342          * @param {Store} this
13343          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13344          */
13345         beforeload : true,
13346         /**
13347          * @event beforeloadadd
13348          * Fires after a new set of Records has been loaded.
13349          * @param {Store} this
13350          * @param {Roo.data.Record[]} records The Records that were loaded
13351          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13352          */
13353         beforeloadadd : true,
13354         /**
13355          * @event load
13356          * Fires after a new set of Records has been loaded, before they are added to the store.
13357          * @param {Store} this
13358          * @param {Roo.data.Record[]} records The Records that were loaded
13359          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13360          * @params {Object} return from reader
13361          */
13362         load : true,
13363         /**
13364          * @event loadexception
13365          * Fires if an exception occurs in the Proxy during loading.
13366          * Called with the signature of the Proxy's "loadexception" event.
13367          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13368          * 
13369          * @param {Proxy} 
13370          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13371          * @param {Object} load options 
13372          * @param {Object} jsonData from your request (normally this contains the Exception)
13373          */
13374         loadexception : true
13375     });
13376     
13377     if(this.proxy){
13378         this.proxy = Roo.factory(this.proxy, Roo.data);
13379         this.proxy.xmodule = this.xmodule || false;
13380         this.relayEvents(this.proxy,  ["loadexception"]);
13381     }
13382     this.sortToggle = {};
13383     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13384
13385     Roo.data.Store.superclass.constructor.call(this);
13386
13387     if(this.inlineData){
13388         this.loadData(this.inlineData);
13389         delete this.inlineData;
13390     }
13391 };
13392
13393 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13394      /**
13395     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13396     * without a remote query - used by combo/forms at present.
13397     */
13398     
13399     /**
13400     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13401     */
13402     /**
13403     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13404     */
13405     /**
13406     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13407     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13408     */
13409     /**
13410     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13411     * on any HTTP request
13412     */
13413     /**
13414     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13415     */
13416     /**
13417     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13418     */
13419     multiSort: false,
13420     /**
13421     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13422     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13423     */
13424     remoteSort : false,
13425
13426     /**
13427     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13428      * loaded or when a record is removed. (defaults to false).
13429     */
13430     pruneModifiedRecords : false,
13431
13432     // private
13433     lastOptions : null,
13434
13435     /**
13436      * Add Records to the Store and fires the add event.
13437      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13438      */
13439     add : function(records){
13440         records = [].concat(records);
13441         for(var i = 0, len = records.length; i < len; i++){
13442             records[i].join(this);
13443         }
13444         var index = this.data.length;
13445         this.data.addAll(records);
13446         this.fireEvent("add", this, records, index);
13447     },
13448
13449     /**
13450      * Remove a Record from the Store and fires the remove event.
13451      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13452      */
13453     remove : function(record){
13454         var index = this.data.indexOf(record);
13455         this.data.removeAt(index);
13456  
13457         if(this.pruneModifiedRecords){
13458             this.modified.remove(record);
13459         }
13460         this.fireEvent("remove", this, record, index);
13461     },
13462
13463     /**
13464      * Remove all Records from the Store and fires the clear event.
13465      */
13466     removeAll : function(){
13467         this.data.clear();
13468         if(this.pruneModifiedRecords){
13469             this.modified = [];
13470         }
13471         this.fireEvent("clear", this);
13472     },
13473
13474     /**
13475      * Inserts Records to the Store at the given index and fires the add event.
13476      * @param {Number} index The start index at which to insert the passed Records.
13477      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13478      */
13479     insert : function(index, records){
13480         records = [].concat(records);
13481         for(var i = 0, len = records.length; i < len; i++){
13482             this.data.insert(index, records[i]);
13483             records[i].join(this);
13484         }
13485         this.fireEvent("add", this, records, index);
13486     },
13487
13488     /**
13489      * Get the index within the cache of the passed Record.
13490      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13491      * @return {Number} The index of the passed Record. Returns -1 if not found.
13492      */
13493     indexOf : function(record){
13494         return this.data.indexOf(record);
13495     },
13496
13497     /**
13498      * Get the index within the cache of the Record with the passed id.
13499      * @param {String} id The id of the Record to find.
13500      * @return {Number} The index of the Record. Returns -1 if not found.
13501      */
13502     indexOfId : function(id){
13503         return this.data.indexOfKey(id);
13504     },
13505
13506     /**
13507      * Get the Record with the specified id.
13508      * @param {String} id The id of the Record to find.
13509      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13510      */
13511     getById : function(id){
13512         return this.data.key(id);
13513     },
13514
13515     /**
13516      * Get the Record at the specified index.
13517      * @param {Number} index The index of the Record to find.
13518      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13519      */
13520     getAt : function(index){
13521         return this.data.itemAt(index);
13522     },
13523
13524     /**
13525      * Returns a range of Records between specified indices.
13526      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13527      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13528      * @return {Roo.data.Record[]} An array of Records
13529      */
13530     getRange : function(start, end){
13531         return this.data.getRange(start, end);
13532     },
13533
13534     // private
13535     storeOptions : function(o){
13536         o = Roo.apply({}, o);
13537         delete o.callback;
13538         delete o.scope;
13539         this.lastOptions = o;
13540     },
13541
13542     /**
13543      * Loads the Record cache from the configured Proxy using the configured Reader.
13544      * <p>
13545      * If using remote paging, then the first load call must specify the <em>start</em>
13546      * and <em>limit</em> properties in the options.params property to establish the initial
13547      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13548      * <p>
13549      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13550      * and this call will return before the new data has been loaded. Perform any post-processing
13551      * in a callback function, or in a "load" event handler.</strong>
13552      * <p>
13553      * @param {Object} options An object containing properties which control loading options:<ul>
13554      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13555      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13556      * passed the following arguments:<ul>
13557      * <li>r : Roo.data.Record[]</li>
13558      * <li>options: Options object from the load call</li>
13559      * <li>success: Boolean success indicator</li></ul></li>
13560      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13561      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13562      * </ul>
13563      */
13564     load : function(options){
13565         options = options || {};
13566         if(this.fireEvent("beforeload", this, options) !== false){
13567             this.storeOptions(options);
13568             var p = Roo.apply(options.params || {}, this.baseParams);
13569             // if meta was not loaded from remote source.. try requesting it.
13570             if (!this.reader.metaFromRemote) {
13571                 p._requestMeta = 1;
13572             }
13573             if(this.sortInfo && this.remoteSort){
13574                 var pn = this.paramNames;
13575                 p[pn["sort"]] = this.sortInfo.field;
13576                 p[pn["dir"]] = this.sortInfo.direction;
13577             }
13578             if (this.multiSort) {
13579                 var pn = this.paramNames;
13580                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13581             }
13582             
13583             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13584         }
13585     },
13586
13587     /**
13588      * Reloads the Record cache from the configured Proxy using the configured Reader and
13589      * the options from the last load operation performed.
13590      * @param {Object} options (optional) An object containing properties which may override the options
13591      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13592      * the most recently used options are reused).
13593      */
13594     reload : function(options){
13595         this.load(Roo.applyIf(options||{}, this.lastOptions));
13596     },
13597
13598     // private
13599     // Called as a callback by the Reader during a load operation.
13600     loadRecords : function(o, options, success){
13601         if(!o || success === false){
13602             if(success !== false){
13603                 this.fireEvent("load", this, [], options, o);
13604             }
13605             if(options.callback){
13606                 options.callback.call(options.scope || this, [], options, false);
13607             }
13608             return;
13609         }
13610         // if data returned failure - throw an exception.
13611         if (o.success === false) {
13612             // show a message if no listener is registered.
13613             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13614                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13615             }
13616             // loadmask wil be hooked into this..
13617             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13618             return;
13619         }
13620         var r = o.records, t = o.totalRecords || r.length;
13621         
13622         this.fireEvent("beforeloadadd", this, r, options, o);
13623         
13624         if(!options || options.add !== true){
13625             if(this.pruneModifiedRecords){
13626                 this.modified = [];
13627             }
13628             for(var i = 0, len = r.length; i < len; i++){
13629                 r[i].join(this);
13630             }
13631             if(this.snapshot){
13632                 this.data = this.snapshot;
13633                 delete this.snapshot;
13634             }
13635             this.data.clear();
13636             this.data.addAll(r);
13637             this.totalLength = t;
13638             this.applySort();
13639             this.fireEvent("datachanged", this);
13640         }else{
13641             this.totalLength = Math.max(t, this.data.length+r.length);
13642             this.add(r);
13643         }
13644         
13645         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13646                 
13647             var e = new Roo.data.Record({});
13648
13649             e.set(this.parent.displayField, this.parent.emptyTitle);
13650             e.set(this.parent.valueField, '');
13651
13652             this.insert(0, e);
13653         }
13654             
13655         this.fireEvent("load", this, r, options, o);
13656         if(options.callback){
13657             options.callback.call(options.scope || this, r, options, true);
13658         }
13659     },
13660
13661
13662     /**
13663      * Loads data from a passed data block. A Reader which understands the format of the data
13664      * must have been configured in the constructor.
13665      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13666      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13667      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13668      */
13669     loadData : function(o, append){
13670         var r = this.reader.readRecords(o);
13671         this.loadRecords(r, {add: append}, true);
13672     },
13673     
13674      /**
13675      * using 'cn' the nested child reader read the child array into it's child stores.
13676      * @param {Object} rec The record with a 'children array
13677      */
13678     loadDataFromChildren : function(rec)
13679     {
13680         this.loadData(this.reader.toLoadData(rec));
13681     },
13682     
13683
13684     /**
13685      * Gets the number of cached records.
13686      * <p>
13687      * <em>If using paging, this may not be the total size of the dataset. If the data object
13688      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13689      * the data set size</em>
13690      */
13691     getCount : function(){
13692         return this.data.length || 0;
13693     },
13694
13695     /**
13696      * Gets the total number of records in the dataset as returned by the server.
13697      * <p>
13698      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13699      * the dataset size</em>
13700      */
13701     getTotalCount : function(){
13702         return this.totalLength || 0;
13703     },
13704
13705     /**
13706      * Returns the sort state of the Store as an object with two properties:
13707      * <pre><code>
13708  field {String} The name of the field by which the Records are sorted
13709  direction {String} The sort order, "ASC" or "DESC"
13710      * </code></pre>
13711      */
13712     getSortState : function(){
13713         return this.sortInfo;
13714     },
13715
13716     // private
13717     applySort : function(){
13718         if(this.sortInfo && !this.remoteSort){
13719             var s = this.sortInfo, f = s.field;
13720             var st = this.fields.get(f).sortType;
13721             var fn = function(r1, r2){
13722                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13723                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13724             };
13725             this.data.sort(s.direction, fn);
13726             if(this.snapshot && this.snapshot != this.data){
13727                 this.snapshot.sort(s.direction, fn);
13728             }
13729         }
13730     },
13731
13732     /**
13733      * Sets the default sort column and order to be used by the next load operation.
13734      * @param {String} fieldName The name of the field to sort by.
13735      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13736      */
13737     setDefaultSort : function(field, dir){
13738         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13739     },
13740
13741     /**
13742      * Sort the Records.
13743      * If remote sorting is used, the sort is performed on the server, and the cache is
13744      * reloaded. If local sorting is used, the cache is sorted internally.
13745      * @param {String} fieldName The name of the field to sort by.
13746      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13747      */
13748     sort : function(fieldName, dir){
13749         var f = this.fields.get(fieldName);
13750         if(!dir){
13751             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13752             
13753             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13754                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13755             }else{
13756                 dir = f.sortDir;
13757             }
13758         }
13759         this.sortToggle[f.name] = dir;
13760         this.sortInfo = {field: f.name, direction: dir};
13761         if(!this.remoteSort){
13762             this.applySort();
13763             this.fireEvent("datachanged", this);
13764         }else{
13765             this.load(this.lastOptions);
13766         }
13767     },
13768
13769     /**
13770      * Calls the specified function for each of the Records in the cache.
13771      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13772      * Returning <em>false</em> aborts and exits the iteration.
13773      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13774      */
13775     each : function(fn, scope){
13776         this.data.each(fn, scope);
13777     },
13778
13779     /**
13780      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13781      * (e.g., during paging).
13782      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13783      */
13784     getModifiedRecords : function(){
13785         return this.modified;
13786     },
13787
13788     // private
13789     createFilterFn : function(property, value, anyMatch){
13790         if(!value.exec){ // not a regex
13791             value = String(value);
13792             if(value.length == 0){
13793                 return false;
13794             }
13795             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13796         }
13797         return function(r){
13798             return value.test(r.data[property]);
13799         };
13800     },
13801
13802     /**
13803      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13804      * @param {String} property A field on your records
13805      * @param {Number} start The record index to start at (defaults to 0)
13806      * @param {Number} end The last record index to include (defaults to length - 1)
13807      * @return {Number} The sum
13808      */
13809     sum : function(property, start, end){
13810         var rs = this.data.items, v = 0;
13811         start = start || 0;
13812         end = (end || end === 0) ? end : rs.length-1;
13813
13814         for(var i = start; i <= end; i++){
13815             v += (rs[i].data[property] || 0);
13816         }
13817         return v;
13818     },
13819
13820     /**
13821      * Filter the records by a specified property.
13822      * @param {String} field A field on your records
13823      * @param {String/RegExp} value Either a string that the field
13824      * should start with or a RegExp to test against the field
13825      * @param {Boolean} anyMatch True to match any part not just the beginning
13826      */
13827     filter : function(property, value, anyMatch){
13828         var fn = this.createFilterFn(property, value, anyMatch);
13829         return fn ? this.filterBy(fn) : this.clearFilter();
13830     },
13831
13832     /**
13833      * Filter by a function. The specified function will be called with each
13834      * record in this data source. If the function returns true the record is included,
13835      * otherwise it is filtered.
13836      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13837      * @param {Object} scope (optional) The scope of the function (defaults to this)
13838      */
13839     filterBy : function(fn, scope){
13840         this.snapshot = this.snapshot || this.data;
13841         this.data = this.queryBy(fn, scope||this);
13842         this.fireEvent("datachanged", this);
13843     },
13844
13845     /**
13846      * Query the records by a specified property.
13847      * @param {String} field A field on your records
13848      * @param {String/RegExp} value Either a string that the field
13849      * should start with or a RegExp to test against the field
13850      * @param {Boolean} anyMatch True to match any part not just the beginning
13851      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13852      */
13853     query : function(property, value, anyMatch){
13854         var fn = this.createFilterFn(property, value, anyMatch);
13855         return fn ? this.queryBy(fn) : this.data.clone();
13856     },
13857
13858     /**
13859      * Query by a function. The specified function will be called with each
13860      * record in this data source. If the function returns true the record is included
13861      * in the results.
13862      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13863      * @param {Object} scope (optional) The scope of the function (defaults to this)
13864       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13865      **/
13866     queryBy : function(fn, scope){
13867         var data = this.snapshot || this.data;
13868         return data.filterBy(fn, scope||this);
13869     },
13870
13871     /**
13872      * Collects unique values for a particular dataIndex from this store.
13873      * @param {String} dataIndex The property to collect
13874      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13875      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13876      * @return {Array} An array of the unique values
13877      **/
13878     collect : function(dataIndex, allowNull, bypassFilter){
13879         var d = (bypassFilter === true && this.snapshot) ?
13880                 this.snapshot.items : this.data.items;
13881         var v, sv, r = [], l = {};
13882         for(var i = 0, len = d.length; i < len; i++){
13883             v = d[i].data[dataIndex];
13884             sv = String(v);
13885             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13886                 l[sv] = true;
13887                 r[r.length] = v;
13888             }
13889         }
13890         return r;
13891     },
13892
13893     /**
13894      * Revert to a view of the Record cache with no filtering applied.
13895      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13896      */
13897     clearFilter : function(suppressEvent){
13898         if(this.snapshot && this.snapshot != this.data){
13899             this.data = this.snapshot;
13900             delete this.snapshot;
13901             if(suppressEvent !== true){
13902                 this.fireEvent("datachanged", this);
13903             }
13904         }
13905     },
13906
13907     // private
13908     afterEdit : function(record){
13909         if(this.modified.indexOf(record) == -1){
13910             this.modified.push(record);
13911         }
13912         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13913     },
13914     
13915     // private
13916     afterReject : function(record){
13917         this.modified.remove(record);
13918         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13919     },
13920
13921     // private
13922     afterCommit : function(record){
13923         this.modified.remove(record);
13924         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13925     },
13926
13927     /**
13928      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13929      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13930      */
13931     commitChanges : function(){
13932         var m = this.modified.slice(0);
13933         this.modified = [];
13934         for(var i = 0, len = m.length; i < len; i++){
13935             m[i].commit();
13936         }
13937     },
13938
13939     /**
13940      * Cancel outstanding changes on all changed records.
13941      */
13942     rejectChanges : function(){
13943         var m = this.modified.slice(0);
13944         this.modified = [];
13945         for(var i = 0, len = m.length; i < len; i++){
13946             m[i].reject();
13947         }
13948     },
13949
13950     onMetaChange : function(meta, rtype, o){
13951         this.recordType = rtype;
13952         this.fields = rtype.prototype.fields;
13953         delete this.snapshot;
13954         this.sortInfo = meta.sortInfo || this.sortInfo;
13955         this.modified = [];
13956         this.fireEvent('metachange', this, this.reader.meta);
13957     },
13958     
13959     moveIndex : function(data, type)
13960     {
13961         var index = this.indexOf(data);
13962         
13963         var newIndex = index + type;
13964         
13965         this.remove(data);
13966         
13967         this.insert(newIndex, data);
13968         
13969     }
13970 });/*
13971  * Based on:
13972  * Ext JS Library 1.1.1
13973  * Copyright(c) 2006-2007, Ext JS, LLC.
13974  *
13975  * Originally Released Under LGPL - original licence link has changed is not relivant.
13976  *
13977  * Fork - LGPL
13978  * <script type="text/javascript">
13979  */
13980
13981 /**
13982  * @class Roo.data.SimpleStore
13983  * @extends Roo.data.Store
13984  * Small helper class to make creating Stores from Array data easier.
13985  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13986  * @cfg {Array} fields An array of field definition objects, or field name strings.
13987  * @cfg {Object} an existing reader (eg. copied from another store)
13988  * @cfg {Array} data The multi-dimensional array of data
13989  * @constructor
13990  * @param {Object} config
13991  */
13992 Roo.data.SimpleStore = function(config)
13993 {
13994     Roo.data.SimpleStore.superclass.constructor.call(this, {
13995         isLocal : true,
13996         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13997                 id: config.id
13998             },
13999             Roo.data.Record.create(config.fields)
14000         ),
14001         proxy : new Roo.data.MemoryProxy(config.data)
14002     });
14003     this.load();
14004 };
14005 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14006  * Based on:
14007  * Ext JS Library 1.1.1
14008  * Copyright(c) 2006-2007, Ext JS, LLC.
14009  *
14010  * Originally Released Under LGPL - original licence link has changed is not relivant.
14011  *
14012  * Fork - LGPL
14013  * <script type="text/javascript">
14014  */
14015
14016 /**
14017 /**
14018  * @extends Roo.data.Store
14019  * @class Roo.data.JsonStore
14020  * Small helper class to make creating Stores for JSON data easier. <br/>
14021 <pre><code>
14022 var store = new Roo.data.JsonStore({
14023     url: 'get-images.php',
14024     root: 'images',
14025     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14026 });
14027 </code></pre>
14028  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14029  * JsonReader and HttpProxy (unless inline data is provided).</b>
14030  * @cfg {Array} fields An array of field definition objects, or field name strings.
14031  * @constructor
14032  * @param {Object} config
14033  */
14034 Roo.data.JsonStore = function(c){
14035     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14036         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14037         reader: new Roo.data.JsonReader(c, c.fields)
14038     }));
14039 };
14040 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14041  * Based on:
14042  * Ext JS Library 1.1.1
14043  * Copyright(c) 2006-2007, Ext JS, LLC.
14044  *
14045  * Originally Released Under LGPL - original licence link has changed is not relivant.
14046  *
14047  * Fork - LGPL
14048  * <script type="text/javascript">
14049  */
14050
14051  
14052 Roo.data.Field = function(config){
14053     if(typeof config == "string"){
14054         config = {name: config};
14055     }
14056     Roo.apply(this, config);
14057     
14058     if(!this.type){
14059         this.type = "auto";
14060     }
14061     
14062     var st = Roo.data.SortTypes;
14063     // named sortTypes are supported, here we look them up
14064     if(typeof this.sortType == "string"){
14065         this.sortType = st[this.sortType];
14066     }
14067     
14068     // set default sortType for strings and dates
14069     if(!this.sortType){
14070         switch(this.type){
14071             case "string":
14072                 this.sortType = st.asUCString;
14073                 break;
14074             case "date":
14075                 this.sortType = st.asDate;
14076                 break;
14077             default:
14078                 this.sortType = st.none;
14079         }
14080     }
14081
14082     // define once
14083     var stripRe = /[\$,%]/g;
14084
14085     // prebuilt conversion function for this field, instead of
14086     // switching every time we're reading a value
14087     if(!this.convert){
14088         var cv, dateFormat = this.dateFormat;
14089         switch(this.type){
14090             case "":
14091             case "auto":
14092             case undefined:
14093                 cv = function(v){ return v; };
14094                 break;
14095             case "string":
14096                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14097                 break;
14098             case "int":
14099                 cv = function(v){
14100                     return v !== undefined && v !== null && v !== '' ?
14101                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14102                     };
14103                 break;
14104             case "float":
14105                 cv = function(v){
14106                     return v !== undefined && v !== null && v !== '' ?
14107                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14108                     };
14109                 break;
14110             case "bool":
14111             case "boolean":
14112                 cv = function(v){ return v === true || v === "true" || v == 1; };
14113                 break;
14114             case "date":
14115                 cv = function(v){
14116                     if(!v){
14117                         return '';
14118                     }
14119                     if(v instanceof Date){
14120                         return v;
14121                     }
14122                     if(dateFormat){
14123                         if(dateFormat == "timestamp"){
14124                             return new Date(v*1000);
14125                         }
14126                         return Date.parseDate(v, dateFormat);
14127                     }
14128                     var parsed = Date.parse(v);
14129                     return parsed ? new Date(parsed) : null;
14130                 };
14131              break;
14132             
14133         }
14134         this.convert = cv;
14135     }
14136 };
14137
14138 Roo.data.Field.prototype = {
14139     dateFormat: null,
14140     defaultValue: "",
14141     mapping: null,
14142     sortType : null,
14143     sortDir : "ASC"
14144 };/*
14145  * Based on:
14146  * Ext JS Library 1.1.1
14147  * Copyright(c) 2006-2007, Ext JS, LLC.
14148  *
14149  * Originally Released Under LGPL - original licence link has changed is not relivant.
14150  *
14151  * Fork - LGPL
14152  * <script type="text/javascript">
14153  */
14154  
14155 // Base class for reading structured data from a data source.  This class is intended to be
14156 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14157
14158 /**
14159  * @class Roo.data.DataReader
14160  * Base class for reading structured data from a data source.  This class is intended to be
14161  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14162  */
14163
14164 Roo.data.DataReader = function(meta, recordType){
14165     
14166     this.meta = meta;
14167     
14168     this.recordType = recordType instanceof Array ? 
14169         Roo.data.Record.create(recordType) : recordType;
14170 };
14171
14172 Roo.data.DataReader.prototype = {
14173     
14174     
14175     readerType : 'Data',
14176      /**
14177      * Create an empty record
14178      * @param {Object} data (optional) - overlay some values
14179      * @return {Roo.data.Record} record created.
14180      */
14181     newRow :  function(d) {
14182         var da =  {};
14183         this.recordType.prototype.fields.each(function(c) {
14184             switch( c.type) {
14185                 case 'int' : da[c.name] = 0; break;
14186                 case 'date' : da[c.name] = new Date(); break;
14187                 case 'float' : da[c.name] = 0.0; break;
14188                 case 'boolean' : da[c.name] = false; break;
14189                 default : da[c.name] = ""; break;
14190             }
14191             
14192         });
14193         return new this.recordType(Roo.apply(da, d));
14194     }
14195     
14196     
14197 };/*
14198  * Based on:
14199  * Ext JS Library 1.1.1
14200  * Copyright(c) 2006-2007, Ext JS, LLC.
14201  *
14202  * Originally Released Under LGPL - original licence link has changed is not relivant.
14203  *
14204  * Fork - LGPL
14205  * <script type="text/javascript">
14206  */
14207
14208 /**
14209  * @class Roo.data.DataProxy
14210  * @extends Roo.data.Observable
14211  * This class is an abstract base class for implementations which provide retrieval of
14212  * unformatted data objects.<br>
14213  * <p>
14214  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14215  * (of the appropriate type which knows how to parse the data object) to provide a block of
14216  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14217  * <p>
14218  * Custom implementations must implement the load method as described in
14219  * {@link Roo.data.HttpProxy#load}.
14220  */
14221 Roo.data.DataProxy = function(){
14222     this.addEvents({
14223         /**
14224          * @event beforeload
14225          * Fires before a network request is made to retrieve a data object.
14226          * @param {Object} This DataProxy object.
14227          * @param {Object} params The params parameter to the load function.
14228          */
14229         beforeload : true,
14230         /**
14231          * @event load
14232          * Fires before the load method's callback is called.
14233          * @param {Object} This DataProxy object.
14234          * @param {Object} o The data object.
14235          * @param {Object} arg The callback argument object passed to the load function.
14236          */
14237         load : true,
14238         /**
14239          * @event loadexception
14240          * Fires if an Exception occurs during data retrieval.
14241          * @param {Object} This DataProxy object.
14242          * @param {Object} o The data object.
14243          * @param {Object} arg The callback argument object passed to the load function.
14244          * @param {Object} e The Exception.
14245          */
14246         loadexception : true
14247     });
14248     Roo.data.DataProxy.superclass.constructor.call(this);
14249 };
14250
14251 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14252
14253     /**
14254      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14255      */
14256 /*
14257  * Based on:
14258  * Ext JS Library 1.1.1
14259  * Copyright(c) 2006-2007, Ext JS, LLC.
14260  *
14261  * Originally Released Under LGPL - original licence link has changed is not relivant.
14262  *
14263  * Fork - LGPL
14264  * <script type="text/javascript">
14265  */
14266 /**
14267  * @class Roo.data.MemoryProxy
14268  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14269  * to the Reader when its load method is called.
14270  * @constructor
14271  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14272  */
14273 Roo.data.MemoryProxy = function(data){
14274     if (data.data) {
14275         data = data.data;
14276     }
14277     Roo.data.MemoryProxy.superclass.constructor.call(this);
14278     this.data = data;
14279 };
14280
14281 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14282     
14283     /**
14284      * Load data from the requested source (in this case an in-memory
14285      * data object passed to the constructor), read the data object into
14286      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14287      * process that block using the passed callback.
14288      * @param {Object} params This parameter is not used by the MemoryProxy class.
14289      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14290      * object into a block of Roo.data.Records.
14291      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14292      * The function must be passed <ul>
14293      * <li>The Record block object</li>
14294      * <li>The "arg" argument from the load function</li>
14295      * <li>A boolean success indicator</li>
14296      * </ul>
14297      * @param {Object} scope The scope in which to call the callback
14298      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14299      */
14300     load : function(params, reader, callback, scope, arg){
14301         params = params || {};
14302         var result;
14303         try {
14304             result = reader.readRecords(params.data ? params.data :this.data);
14305         }catch(e){
14306             this.fireEvent("loadexception", this, arg, null, e);
14307             callback.call(scope, null, arg, false);
14308             return;
14309         }
14310         callback.call(scope, result, arg, true);
14311     },
14312     
14313     // private
14314     update : function(params, records){
14315         
14316     }
14317 });/*
14318  * Based on:
14319  * Ext JS Library 1.1.1
14320  * Copyright(c) 2006-2007, Ext JS, LLC.
14321  *
14322  * Originally Released Under LGPL - original licence link has changed is not relivant.
14323  *
14324  * Fork - LGPL
14325  * <script type="text/javascript">
14326  */
14327 /**
14328  * @class Roo.data.HttpProxy
14329  * @extends Roo.data.DataProxy
14330  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14331  * configured to reference a certain URL.<br><br>
14332  * <p>
14333  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14334  * from which the running page was served.<br><br>
14335  * <p>
14336  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14337  * <p>
14338  * Be aware that to enable the browser to parse an XML document, the server must set
14339  * the Content-Type header in the HTTP response to "text/xml".
14340  * @constructor
14341  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14342  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14343  * will be used to make the request.
14344  */
14345 Roo.data.HttpProxy = function(conn){
14346     Roo.data.HttpProxy.superclass.constructor.call(this);
14347     // is conn a conn config or a real conn?
14348     this.conn = conn;
14349     this.useAjax = !conn || !conn.events;
14350   
14351 };
14352
14353 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14354     // thse are take from connection...
14355     
14356     /**
14357      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14358      */
14359     /**
14360      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14361      * extra parameters to each request made by this object. (defaults to undefined)
14362      */
14363     /**
14364      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14365      *  to each request made by this object. (defaults to undefined)
14366      */
14367     /**
14368      * @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)
14369      */
14370     /**
14371      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14372      */
14373      /**
14374      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14375      * @type Boolean
14376      */
14377   
14378
14379     /**
14380      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14381      * @type Boolean
14382      */
14383     /**
14384      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14385      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14386      * a finer-grained basis than the DataProxy events.
14387      */
14388     getConnection : function(){
14389         return this.useAjax ? Roo.Ajax : this.conn;
14390     },
14391
14392     /**
14393      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14394      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14395      * process that block using the passed callback.
14396      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14397      * for the request to the remote server.
14398      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14399      * object into a block of Roo.data.Records.
14400      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14401      * The function must be passed <ul>
14402      * <li>The Record block object</li>
14403      * <li>The "arg" argument from the load function</li>
14404      * <li>A boolean success indicator</li>
14405      * </ul>
14406      * @param {Object} scope The scope in which to call the callback
14407      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14408      */
14409     load : function(params, reader, callback, scope, arg){
14410         if(this.fireEvent("beforeload", this, params) !== false){
14411             var  o = {
14412                 params : params || {},
14413                 request: {
14414                     callback : callback,
14415                     scope : scope,
14416                     arg : arg
14417                 },
14418                 reader: reader,
14419                 callback : this.loadResponse,
14420                 scope: this
14421             };
14422             if(this.useAjax){
14423                 Roo.applyIf(o, this.conn);
14424                 if(this.activeRequest){
14425                     Roo.Ajax.abort(this.activeRequest);
14426                 }
14427                 this.activeRequest = Roo.Ajax.request(o);
14428             }else{
14429                 this.conn.request(o);
14430             }
14431         }else{
14432             callback.call(scope||this, null, arg, false);
14433         }
14434     },
14435
14436     // private
14437     loadResponse : function(o, success, response){
14438         delete this.activeRequest;
14439         if(!success){
14440             this.fireEvent("loadexception", this, o, response);
14441             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14442             return;
14443         }
14444         var result;
14445         try {
14446             result = o.reader.read(response);
14447         }catch(e){
14448             this.fireEvent("loadexception", this, o, response, e);
14449             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14450             return;
14451         }
14452         
14453         this.fireEvent("load", this, o, o.request.arg);
14454         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14455     },
14456
14457     // private
14458     update : function(dataSet){
14459
14460     },
14461
14462     // private
14463     updateResponse : function(dataSet){
14464
14465     }
14466 });/*
14467  * Based on:
14468  * Ext JS Library 1.1.1
14469  * Copyright(c) 2006-2007, Ext JS, LLC.
14470  *
14471  * Originally Released Under LGPL - original licence link has changed is not relivant.
14472  *
14473  * Fork - LGPL
14474  * <script type="text/javascript">
14475  */
14476
14477 /**
14478  * @class Roo.data.ScriptTagProxy
14479  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14480  * other than the originating domain of the running page.<br><br>
14481  * <p>
14482  * <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
14483  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14484  * <p>
14485  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14486  * source code that is used as the source inside a &lt;script> tag.<br><br>
14487  * <p>
14488  * In order for the browser to process the returned data, the server must wrap the data object
14489  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14490  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14491  * depending on whether the callback name was passed:
14492  * <p>
14493  * <pre><code>
14494 boolean scriptTag = false;
14495 String cb = request.getParameter("callback");
14496 if (cb != null) {
14497     scriptTag = true;
14498     response.setContentType("text/javascript");
14499 } else {
14500     response.setContentType("application/x-json");
14501 }
14502 Writer out = response.getWriter();
14503 if (scriptTag) {
14504     out.write(cb + "(");
14505 }
14506 out.print(dataBlock.toJsonString());
14507 if (scriptTag) {
14508     out.write(");");
14509 }
14510 </pre></code>
14511  *
14512  * @constructor
14513  * @param {Object} config A configuration object.
14514  */
14515 Roo.data.ScriptTagProxy = function(config){
14516     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14517     Roo.apply(this, config);
14518     this.head = document.getElementsByTagName("head")[0];
14519 };
14520
14521 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14522
14523 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14524     /**
14525      * @cfg {String} url The URL from which to request the data object.
14526      */
14527     /**
14528      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14529      */
14530     timeout : 30000,
14531     /**
14532      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14533      * the server the name of the callback function set up by the load call to process the returned data object.
14534      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14535      * javascript output which calls this named function passing the data object as its only parameter.
14536      */
14537     callbackParam : "callback",
14538     /**
14539      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14540      * name to the request.
14541      */
14542     nocache : true,
14543
14544     /**
14545      * Load data from the configured URL, read the data object into
14546      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14547      * process that block using the passed callback.
14548      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14549      * for the request to the remote server.
14550      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14551      * object into a block of Roo.data.Records.
14552      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14553      * The function must be passed <ul>
14554      * <li>The Record block object</li>
14555      * <li>The "arg" argument from the load function</li>
14556      * <li>A boolean success indicator</li>
14557      * </ul>
14558      * @param {Object} scope The scope in which to call the callback
14559      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14560      */
14561     load : function(params, reader, callback, scope, arg){
14562         if(this.fireEvent("beforeload", this, params) !== false){
14563
14564             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14565
14566             var url = this.url;
14567             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14568             if(this.nocache){
14569                 url += "&_dc=" + (new Date().getTime());
14570             }
14571             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14572             var trans = {
14573                 id : transId,
14574                 cb : "stcCallback"+transId,
14575                 scriptId : "stcScript"+transId,
14576                 params : params,
14577                 arg : arg,
14578                 url : url,
14579                 callback : callback,
14580                 scope : scope,
14581                 reader : reader
14582             };
14583             var conn = this;
14584
14585             window[trans.cb] = function(o){
14586                 conn.handleResponse(o, trans);
14587             };
14588
14589             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14590
14591             if(this.autoAbort !== false){
14592                 this.abort();
14593             }
14594
14595             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14596
14597             var script = document.createElement("script");
14598             script.setAttribute("src", url);
14599             script.setAttribute("type", "text/javascript");
14600             script.setAttribute("id", trans.scriptId);
14601             this.head.appendChild(script);
14602
14603             this.trans = trans;
14604         }else{
14605             callback.call(scope||this, null, arg, false);
14606         }
14607     },
14608
14609     // private
14610     isLoading : function(){
14611         return this.trans ? true : false;
14612     },
14613
14614     /**
14615      * Abort the current server request.
14616      */
14617     abort : function(){
14618         if(this.isLoading()){
14619             this.destroyTrans(this.trans);
14620         }
14621     },
14622
14623     // private
14624     destroyTrans : function(trans, isLoaded){
14625         this.head.removeChild(document.getElementById(trans.scriptId));
14626         clearTimeout(trans.timeoutId);
14627         if(isLoaded){
14628             window[trans.cb] = undefined;
14629             try{
14630                 delete window[trans.cb];
14631             }catch(e){}
14632         }else{
14633             // if hasn't been loaded, wait for load to remove it to prevent script error
14634             window[trans.cb] = function(){
14635                 window[trans.cb] = undefined;
14636                 try{
14637                     delete window[trans.cb];
14638                 }catch(e){}
14639             };
14640         }
14641     },
14642
14643     // private
14644     handleResponse : function(o, trans){
14645         this.trans = false;
14646         this.destroyTrans(trans, true);
14647         var result;
14648         try {
14649             result = trans.reader.readRecords(o);
14650         }catch(e){
14651             this.fireEvent("loadexception", this, o, trans.arg, e);
14652             trans.callback.call(trans.scope||window, null, trans.arg, false);
14653             return;
14654         }
14655         this.fireEvent("load", this, o, trans.arg);
14656         trans.callback.call(trans.scope||window, result, trans.arg, true);
14657     },
14658
14659     // private
14660     handleFailure : function(trans){
14661         this.trans = false;
14662         this.destroyTrans(trans, false);
14663         this.fireEvent("loadexception", this, null, trans.arg);
14664         trans.callback.call(trans.scope||window, null, trans.arg, false);
14665     }
14666 });/*
14667  * Based on:
14668  * Ext JS Library 1.1.1
14669  * Copyright(c) 2006-2007, Ext JS, LLC.
14670  *
14671  * Originally Released Under LGPL - original licence link has changed is not relivant.
14672  *
14673  * Fork - LGPL
14674  * <script type="text/javascript">
14675  */
14676
14677 /**
14678  * @class Roo.data.JsonReader
14679  * @extends Roo.data.DataReader
14680  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14681  * based on mappings in a provided Roo.data.Record constructor.
14682  * 
14683  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14684  * in the reply previously. 
14685  * 
14686  * <p>
14687  * Example code:
14688  * <pre><code>
14689 var RecordDef = Roo.data.Record.create([
14690     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14691     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14692 ]);
14693 var myReader = new Roo.data.JsonReader({
14694     totalProperty: "results",    // The property which contains the total dataset size (optional)
14695     root: "rows",                // The property which contains an Array of row objects
14696     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14697 }, RecordDef);
14698 </code></pre>
14699  * <p>
14700  * This would consume a JSON file like this:
14701  * <pre><code>
14702 { 'results': 2, 'rows': [
14703     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14704     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14705 }
14706 </code></pre>
14707  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14708  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14709  * paged from the remote server.
14710  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14711  * @cfg {String} root name of the property which contains the Array of row objects.
14712  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14713  * @cfg {Array} fields Array of field definition objects
14714  * @constructor
14715  * Create a new JsonReader
14716  * @param {Object} meta Metadata configuration options
14717  * @param {Object} recordType Either an Array of field definition objects,
14718  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14719  */
14720 Roo.data.JsonReader = function(meta, recordType){
14721     
14722     meta = meta || {};
14723     // set some defaults:
14724     Roo.applyIf(meta, {
14725         totalProperty: 'total',
14726         successProperty : 'success',
14727         root : 'data',
14728         id : 'id'
14729     });
14730     
14731     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14732 };
14733 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14734     
14735     readerType : 'Json',
14736     
14737     /**
14738      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14739      * Used by Store query builder to append _requestMeta to params.
14740      * 
14741      */
14742     metaFromRemote : false,
14743     /**
14744      * This method is only used by a DataProxy which has retrieved data from a remote server.
14745      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14746      * @return {Object} data A data block which is used by an Roo.data.Store object as
14747      * a cache of Roo.data.Records.
14748      */
14749     read : function(response){
14750         var json = response.responseText;
14751        
14752         var o = /* eval:var:o */ eval("("+json+")");
14753         if(!o) {
14754             throw {message: "JsonReader.read: Json object not found"};
14755         }
14756         
14757         if(o.metaData){
14758             
14759             delete this.ef;
14760             this.metaFromRemote = true;
14761             this.meta = o.metaData;
14762             this.recordType = Roo.data.Record.create(o.metaData.fields);
14763             this.onMetaChange(this.meta, this.recordType, o);
14764         }
14765         return this.readRecords(o);
14766     },
14767
14768     // private function a store will implement
14769     onMetaChange : function(meta, recordType, o){
14770
14771     },
14772
14773     /**
14774          * @ignore
14775          */
14776     simpleAccess: function(obj, subsc) {
14777         return obj[subsc];
14778     },
14779
14780         /**
14781          * @ignore
14782          */
14783     getJsonAccessor: function(){
14784         var re = /[\[\.]/;
14785         return function(expr) {
14786             try {
14787                 return(re.test(expr))
14788                     ? new Function("obj", "return obj." + expr)
14789                     : function(obj){
14790                         return obj[expr];
14791                     };
14792             } catch(e){}
14793             return Roo.emptyFn;
14794         };
14795     }(),
14796
14797     /**
14798      * Create a data block containing Roo.data.Records from an XML document.
14799      * @param {Object} o An object which contains an Array of row objects in the property specified
14800      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14801      * which contains the total size of the dataset.
14802      * @return {Object} data A data block which is used by an Roo.data.Store object as
14803      * a cache of Roo.data.Records.
14804      */
14805     readRecords : function(o){
14806         /**
14807          * After any data loads, the raw JSON data is available for further custom processing.
14808          * @type Object
14809          */
14810         this.o = o;
14811         var s = this.meta, Record = this.recordType,
14812             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14813
14814 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14815         if (!this.ef) {
14816             if(s.totalProperty) {
14817                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14818                 }
14819                 if(s.successProperty) {
14820                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14821                 }
14822                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14823                 if (s.id) {
14824                         var g = this.getJsonAccessor(s.id);
14825                         this.getId = function(rec) {
14826                                 var r = g(rec);  
14827                                 return (r === undefined || r === "") ? null : r;
14828                         };
14829                 } else {
14830                         this.getId = function(){return null;};
14831                 }
14832             this.ef = [];
14833             for(var jj = 0; jj < fl; jj++){
14834                 f = fi[jj];
14835                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14836                 this.ef[jj] = this.getJsonAccessor(map);
14837             }
14838         }
14839
14840         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14841         if(s.totalProperty){
14842             var vt = parseInt(this.getTotal(o), 10);
14843             if(!isNaN(vt)){
14844                 totalRecords = vt;
14845             }
14846         }
14847         if(s.successProperty){
14848             var vs = this.getSuccess(o);
14849             if(vs === false || vs === 'false'){
14850                 success = false;
14851             }
14852         }
14853         var records = [];
14854         for(var i = 0; i < c; i++){
14855                 var n = root[i];
14856             var values = {};
14857             var id = this.getId(n);
14858             for(var j = 0; j < fl; j++){
14859                 f = fi[j];
14860             var v = this.ef[j](n);
14861             if (!f.convert) {
14862                 Roo.log('missing convert for ' + f.name);
14863                 Roo.log(f);
14864                 continue;
14865             }
14866             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14867             }
14868             var record = new Record(values, id);
14869             record.json = n;
14870             records[i] = record;
14871         }
14872         return {
14873             raw : o,
14874             success : success,
14875             records : records,
14876             totalRecords : totalRecords
14877         };
14878     },
14879     // used when loading children.. @see loadDataFromChildren
14880     toLoadData: function(rec)
14881     {
14882         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14883         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14884         return { data : data, total : data.length };
14885         
14886     }
14887 });/*
14888  * Based on:
14889  * Ext JS Library 1.1.1
14890  * Copyright(c) 2006-2007, Ext JS, LLC.
14891  *
14892  * Originally Released Under LGPL - original licence link has changed is not relivant.
14893  *
14894  * Fork - LGPL
14895  * <script type="text/javascript">
14896  */
14897
14898 /**
14899  * @class Roo.data.ArrayReader
14900  * @extends Roo.data.DataReader
14901  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14902  * Each element of that Array represents a row of data fields. The
14903  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14904  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14905  * <p>
14906  * Example code:.
14907  * <pre><code>
14908 var RecordDef = Roo.data.Record.create([
14909     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14910     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14911 ]);
14912 var myReader = new Roo.data.ArrayReader({
14913     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14914 }, RecordDef);
14915 </code></pre>
14916  * <p>
14917  * This would consume an Array like this:
14918  * <pre><code>
14919 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14920   </code></pre>
14921  
14922  * @constructor
14923  * Create a new JsonReader
14924  * @param {Object} meta Metadata configuration options.
14925  * @param {Object|Array} recordType Either an Array of field definition objects
14926  * 
14927  * @cfg {Array} fields Array of field definition objects
14928  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14929  * as specified to {@link Roo.data.Record#create},
14930  * or an {@link Roo.data.Record} object
14931  *
14932  * 
14933  * created using {@link Roo.data.Record#create}.
14934  */
14935 Roo.data.ArrayReader = function(meta, recordType)
14936 {    
14937     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14938 };
14939
14940 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14941     
14942       /**
14943      * Create a data block containing Roo.data.Records from an XML document.
14944      * @param {Object} o An Array of row objects which represents the dataset.
14945      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14946      * a cache of Roo.data.Records.
14947      */
14948     readRecords : function(o)
14949     {
14950         var sid = this.meta ? this.meta.id : null;
14951         var recordType = this.recordType, fields = recordType.prototype.fields;
14952         var records = [];
14953         var root = o;
14954         for(var i = 0; i < root.length; i++){
14955                 var n = root[i];
14956             var values = {};
14957             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14958             for(var j = 0, jlen = fields.length; j < jlen; j++){
14959                 var f = fields.items[j];
14960                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14961                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14962                 v = f.convert(v);
14963                 values[f.name] = v;
14964             }
14965             var record = new recordType(values, id);
14966             record.json = n;
14967             records[records.length] = record;
14968         }
14969         return {
14970             records : records,
14971             totalRecords : records.length
14972         };
14973     },
14974     // used when loading children.. @see loadDataFromChildren
14975     toLoadData: function(rec)
14976     {
14977         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14978         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14979         
14980     }
14981     
14982     
14983 });/*
14984  * - LGPL
14985  * * 
14986  */
14987
14988 /**
14989  * @class Roo.bootstrap.ComboBox
14990  * @extends Roo.bootstrap.TriggerField
14991  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14992  * @cfg {Boolean} append (true|false) default false
14993  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14994  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14995  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14996  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14997  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14998  * @cfg {Boolean} animate default true
14999  * @cfg {Boolean} emptyResultText only for touch device
15000  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15001  * @cfg {String} emptyTitle default ''
15002  * @cfg {Number} width fixed with? experimental
15003  * @constructor
15004  * Create a new ComboBox.
15005  * @param {Object} config Configuration options
15006  */
15007 Roo.bootstrap.ComboBox = function(config){
15008     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15009     this.addEvents({
15010         /**
15011          * @event expand
15012          * Fires when the dropdown list is expanded
15013         * @param {Roo.bootstrap.ComboBox} combo This combo box
15014         */
15015         'expand' : true,
15016         /**
15017          * @event collapse
15018          * Fires when the dropdown list is collapsed
15019         * @param {Roo.bootstrap.ComboBox} combo This combo box
15020         */
15021         'collapse' : true,
15022         /**
15023          * @event beforeselect
15024          * Fires before a list item is selected. Return false to cancel the selection.
15025         * @param {Roo.bootstrap.ComboBox} combo This combo box
15026         * @param {Roo.data.Record} record The data record returned from the underlying store
15027         * @param {Number} index The index of the selected item in the dropdown list
15028         */
15029         'beforeselect' : true,
15030         /**
15031          * @event select
15032          * Fires when a list item is selected
15033         * @param {Roo.bootstrap.ComboBox} combo This combo box
15034         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15035         * @param {Number} index The index of the selected item in the dropdown list
15036         */
15037         'select' : true,
15038         /**
15039          * @event beforequery
15040          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15041          * The event object passed has these properties:
15042         * @param {Roo.bootstrap.ComboBox} combo This combo box
15043         * @param {String} query The query
15044         * @param {Boolean} forceAll true to force "all" query
15045         * @param {Boolean} cancel true to cancel the query
15046         * @param {Object} e The query event object
15047         */
15048         'beforequery': true,
15049          /**
15050          * @event add
15051          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15052         * @param {Roo.bootstrap.ComboBox} combo This combo box
15053         */
15054         'add' : true,
15055         /**
15056          * @event edit
15057          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15058         * @param {Roo.bootstrap.ComboBox} combo This combo box
15059         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15060         */
15061         'edit' : true,
15062         /**
15063          * @event remove
15064          * Fires when the remove value from the combobox array
15065         * @param {Roo.bootstrap.ComboBox} combo This combo box
15066         */
15067         'remove' : true,
15068         /**
15069          * @event afterremove
15070          * Fires when the remove value from the combobox array
15071         * @param {Roo.bootstrap.ComboBox} combo This combo box
15072         */
15073         'afterremove' : true,
15074         /**
15075          * @event specialfilter
15076          * Fires when specialfilter
15077             * @param {Roo.bootstrap.ComboBox} combo This combo box
15078             */
15079         'specialfilter' : true,
15080         /**
15081          * @event tick
15082          * Fires when tick the element
15083             * @param {Roo.bootstrap.ComboBox} combo This combo box
15084             */
15085         'tick' : true,
15086         /**
15087          * @event touchviewdisplay
15088          * Fires when touch view require special display (default is using displayField)
15089             * @param {Roo.bootstrap.ComboBox} combo This combo box
15090             * @param {Object} cfg set html .
15091             */
15092         'touchviewdisplay' : true
15093         
15094     });
15095     
15096     this.item = [];
15097     this.tickItems = [];
15098     
15099     this.selectedIndex = -1;
15100     if(this.mode == 'local'){
15101         if(config.queryDelay === undefined){
15102             this.queryDelay = 10;
15103         }
15104         if(config.minChars === undefined){
15105             this.minChars = 0;
15106         }
15107     }
15108 };
15109
15110 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15111      
15112     /**
15113      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15114      * rendering into an Roo.Editor, defaults to false)
15115      */
15116     /**
15117      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15118      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15119      */
15120     /**
15121      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15122      */
15123     /**
15124      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15125      * the dropdown list (defaults to undefined, with no header element)
15126      */
15127
15128      /**
15129      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15130      */
15131      
15132      /**
15133      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15134      */
15135     listWidth: undefined,
15136     /**
15137      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15138      * mode = 'remote' or 'text' if mode = 'local')
15139      */
15140     displayField: undefined,
15141     
15142     /**
15143      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15144      * mode = 'remote' or 'value' if mode = 'local'). 
15145      * Note: use of a valueField requires the user make a selection
15146      * in order for a value to be mapped.
15147      */
15148     valueField: undefined,
15149     /**
15150      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15151      */
15152     modalTitle : '',
15153     
15154     /**
15155      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15156      * field's data value (defaults to the underlying DOM element's name)
15157      */
15158     hiddenName: undefined,
15159     /**
15160      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15161      */
15162     listClass: '',
15163     /**
15164      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15165      */
15166     selectedClass: 'active',
15167     
15168     /**
15169      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15170      */
15171     shadow:'sides',
15172     /**
15173      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15174      * anchor positions (defaults to 'tl-bl')
15175      */
15176     listAlign: 'tl-bl?',
15177     /**
15178      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15179      */
15180     maxHeight: 300,
15181     /**
15182      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15183      * query specified by the allQuery config option (defaults to 'query')
15184      */
15185     triggerAction: 'query',
15186     /**
15187      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15188      * (defaults to 4, does not apply if editable = false)
15189      */
15190     minChars : 4,
15191     /**
15192      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15193      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15194      */
15195     typeAhead: false,
15196     /**
15197      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15198      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15199      */
15200     queryDelay: 500,
15201     /**
15202      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15203      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15204      */
15205     pageSize: 0,
15206     /**
15207      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15208      * when editable = true (defaults to false)
15209      */
15210     selectOnFocus:false,
15211     /**
15212      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15213      */
15214     queryParam: 'query',
15215     /**
15216      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15217      * when mode = 'remote' (defaults to 'Loading...')
15218      */
15219     loadingText: 'Loading...',
15220     /**
15221      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15222      */
15223     resizable: false,
15224     /**
15225      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15226      */
15227     handleHeight : 8,
15228     /**
15229      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15230      * traditional select (defaults to true)
15231      */
15232     editable: true,
15233     /**
15234      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15235      */
15236     allQuery: '',
15237     /**
15238      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15239      */
15240     mode: 'remote',
15241     /**
15242      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15243      * listWidth has a higher value)
15244      */
15245     minListWidth : 70,
15246     /**
15247      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15248      * allow the user to set arbitrary text into the field (defaults to false)
15249      */
15250     forceSelection:false,
15251     /**
15252      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15253      * if typeAhead = true (defaults to 250)
15254      */
15255     typeAheadDelay : 250,
15256     /**
15257      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15258      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15259      */
15260     valueNotFoundText : undefined,
15261     /**
15262      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15263      */
15264     blockFocus : false,
15265     
15266     /**
15267      * @cfg {Boolean} disableClear Disable showing of clear button.
15268      */
15269     disableClear : false,
15270     /**
15271      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15272      */
15273     alwaysQuery : false,
15274     
15275     /**
15276      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15277      */
15278     multiple : false,
15279     
15280     /**
15281      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15282      */
15283     invalidClass : "has-warning",
15284     
15285     /**
15286      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15287      */
15288     validClass : "has-success",
15289     
15290     /**
15291      * @cfg {Boolean} specialFilter (true|false) special filter default false
15292      */
15293     specialFilter : false,
15294     
15295     /**
15296      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15297      */
15298     mobileTouchView : true,
15299     
15300     /**
15301      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15302      */
15303     useNativeIOS : false,
15304     
15305     /**
15306      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15307      */
15308     mobile_restrict_height : false,
15309     
15310     ios_options : false,
15311     
15312     //private
15313     addicon : false,
15314     editicon: false,
15315     
15316     page: 0,
15317     hasQuery: false,
15318     append: false,
15319     loadNext: false,
15320     autoFocus : true,
15321     tickable : false,
15322     btnPosition : 'right',
15323     triggerList : true,
15324     showToggleBtn : true,
15325     animate : true,
15326     emptyResultText: 'Empty',
15327     triggerText : 'Select',
15328     emptyTitle : '',
15329     width : false,
15330     
15331     // element that contains real text value.. (when hidden is used..)
15332     
15333     getAutoCreate : function()
15334     {   
15335         var cfg = false;
15336         //render
15337         /*
15338          * Render classic select for iso
15339          */
15340         
15341         if(Roo.isIOS && this.useNativeIOS){
15342             cfg = this.getAutoCreateNativeIOS();
15343             return cfg;
15344         }
15345         
15346         /*
15347          * Touch Devices
15348          */
15349         
15350         if(Roo.isTouch && this.mobileTouchView){
15351             cfg = this.getAutoCreateTouchView();
15352             return cfg;;
15353         }
15354         
15355         /*
15356          *  Normal ComboBox
15357          */
15358         if(!this.tickable){
15359             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15360             return cfg;
15361         }
15362         
15363         /*
15364          *  ComboBox with tickable selections
15365          */
15366              
15367         var align = this.labelAlign || this.parentLabelAlign();
15368         
15369         cfg = {
15370             cls : 'form-group roo-combobox-tickable' //input-group
15371         };
15372         
15373         var btn_text_select = '';
15374         var btn_text_done = '';
15375         var btn_text_cancel = '';
15376         
15377         if (this.btn_text_show) {
15378             btn_text_select = 'Select';
15379             btn_text_done = 'Done';
15380             btn_text_cancel = 'Cancel'; 
15381         }
15382         
15383         var buttons = {
15384             tag : 'div',
15385             cls : 'tickable-buttons',
15386             cn : [
15387                 {
15388                     tag : 'button',
15389                     type : 'button',
15390                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15391                     //html : this.triggerText
15392                     html: btn_text_select
15393                 },
15394                 {
15395                     tag : 'button',
15396                     type : 'button',
15397                     name : 'ok',
15398                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15399                     //html : 'Done'
15400                     html: btn_text_done
15401                 },
15402                 {
15403                     tag : 'button',
15404                     type : 'button',
15405                     name : 'cancel',
15406                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15407                     //html : 'Cancel'
15408                     html: btn_text_cancel
15409                 }
15410             ]
15411         };
15412         
15413         if(this.editable){
15414             buttons.cn.unshift({
15415                 tag: 'input',
15416                 cls: 'roo-select2-search-field-input'
15417             });
15418         }
15419         
15420         var _this = this;
15421         
15422         Roo.each(buttons.cn, function(c){
15423             if (_this.size) {
15424                 c.cls += ' btn-' + _this.size;
15425             }
15426
15427             if (_this.disabled) {
15428                 c.disabled = true;
15429             }
15430         });
15431         
15432         var box = {
15433             tag: 'div',
15434             style : 'display: contents',
15435             cn: [
15436                 {
15437                     tag: 'input',
15438                     type : 'hidden',
15439                     cls: 'form-hidden-field'
15440                 },
15441                 {
15442                     tag: 'ul',
15443                     cls: 'roo-select2-choices',
15444                     cn:[
15445                         {
15446                             tag: 'li',
15447                             cls: 'roo-select2-search-field',
15448                             cn: [
15449                                 buttons
15450                             ]
15451                         }
15452                     ]
15453                 }
15454             ]
15455         };
15456         
15457         var combobox = {
15458             cls: 'roo-select2-container input-group roo-select2-container-multi',
15459             cn: [
15460                 
15461                 box
15462 //                {
15463 //                    tag: 'ul',
15464 //                    cls: 'typeahead typeahead-long dropdown-menu',
15465 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15466 //                }
15467             ]
15468         };
15469         
15470         if(this.hasFeedback && !this.allowBlank){
15471             
15472             var feedback = {
15473                 tag: 'span',
15474                 cls: 'glyphicon form-control-feedback'
15475             };
15476
15477             combobox.cn.push(feedback);
15478         }
15479         
15480         
15481         
15482         var indicator = {
15483             tag : 'i',
15484             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15485             tooltip : 'This field is required'
15486         };
15487         if (Roo.bootstrap.version == 4) {
15488             indicator = {
15489                 tag : 'i',
15490                 style : 'display:none'
15491             };
15492         }
15493         if (align ==='left' && this.fieldLabel.length) {
15494             
15495             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15496             
15497             cfg.cn = [
15498                 indicator,
15499                 {
15500                     tag: 'label',
15501                     'for' :  id,
15502                     cls : 'control-label col-form-label',
15503                     html : this.fieldLabel
15504
15505                 },
15506                 {
15507                     cls : "", 
15508                     cn: [
15509                         combobox
15510                     ]
15511                 }
15512
15513             ];
15514             
15515             var labelCfg = cfg.cn[1];
15516             var contentCfg = cfg.cn[2];
15517             
15518
15519             if(this.indicatorpos == 'right'){
15520                 
15521                 cfg.cn = [
15522                     {
15523                         tag: 'label',
15524                         'for' :  id,
15525                         cls : 'control-label col-form-label',
15526                         cn : [
15527                             {
15528                                 tag : 'span',
15529                                 html : this.fieldLabel
15530                             },
15531                             indicator
15532                         ]
15533                     },
15534                     {
15535                         cls : "",
15536                         cn: [
15537                             combobox
15538                         ]
15539                     }
15540
15541                 ];
15542                 
15543                 
15544                 
15545                 labelCfg = cfg.cn[0];
15546                 contentCfg = cfg.cn[1];
15547             
15548             }
15549             
15550             if(this.labelWidth > 12){
15551                 labelCfg.style = "width: " + this.labelWidth + 'px';
15552             }
15553             if(this.width * 1 > 0){
15554                 contentCfg.style = "width: " + this.width + 'px';
15555             }
15556             if(this.labelWidth < 13 && this.labelmd == 0){
15557                 this.labelmd = this.labelWidth;
15558             }
15559             
15560             if(this.labellg > 0){
15561                 labelCfg.cls += ' col-lg-' + this.labellg;
15562                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15563             }
15564             
15565             if(this.labelmd > 0){
15566                 labelCfg.cls += ' col-md-' + this.labelmd;
15567                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15568             }
15569             
15570             if(this.labelsm > 0){
15571                 labelCfg.cls += ' col-sm-' + this.labelsm;
15572                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15573             }
15574             
15575             if(this.labelxs > 0){
15576                 labelCfg.cls += ' col-xs-' + this.labelxs;
15577                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15578             }
15579                 
15580                 
15581         } else if ( this.fieldLabel.length) {
15582 //                Roo.log(" label");
15583                  cfg.cn = [
15584                    indicator,
15585                     {
15586                         tag: 'label',
15587                         //cls : 'input-group-addon',
15588                         html : this.fieldLabel
15589                     },
15590                     combobox
15591                 ];
15592                 
15593                 if(this.indicatorpos == 'right'){
15594                     cfg.cn = [
15595                         {
15596                             tag: 'label',
15597                             //cls : 'input-group-addon',
15598                             html : this.fieldLabel
15599                         },
15600                         indicator,
15601                         combobox
15602                     ];
15603                     
15604                 }
15605
15606         } else {
15607             
15608 //                Roo.log(" no label && no align");
15609                 cfg = combobox
15610                      
15611                 
15612         }
15613          
15614         var settings=this;
15615         ['xs','sm','md','lg'].map(function(size){
15616             if (settings[size]) {
15617                 cfg.cls += ' col-' + size + '-' + settings[size];
15618             }
15619         });
15620         
15621         return cfg;
15622         
15623     },
15624     
15625     _initEventsCalled : false,
15626     
15627     // private
15628     initEvents: function()
15629     {   
15630         if (this._initEventsCalled) { // as we call render... prevent looping...
15631             return;
15632         }
15633         this._initEventsCalled = true;
15634         
15635         if (!this.store) {
15636             throw "can not find store for combo";
15637         }
15638         
15639         this.indicator = this.indicatorEl();
15640         
15641         this.store = Roo.factory(this.store, Roo.data);
15642         this.store.parent = this;
15643         
15644         // if we are building from html. then this element is so complex, that we can not really
15645         // use the rendered HTML.
15646         // so we have to trash and replace the previous code.
15647         if (Roo.XComponent.build_from_html) {
15648             // remove this element....
15649             var e = this.el.dom, k=0;
15650             while (e ) { e = e.previousSibling;  ++k;}
15651
15652             this.el.remove();
15653             
15654             this.el=false;
15655             this.rendered = false;
15656             
15657             this.render(this.parent().getChildContainer(true), k);
15658         }
15659         
15660         if(Roo.isIOS && this.useNativeIOS){
15661             this.initIOSView();
15662             return;
15663         }
15664         
15665         /*
15666          * Touch Devices
15667          */
15668         
15669         if(Roo.isTouch && this.mobileTouchView){
15670             this.initTouchView();
15671             return;
15672         }
15673         
15674         if(this.tickable){
15675             this.initTickableEvents();
15676             return;
15677         }
15678         
15679         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15680         
15681         if(this.hiddenName){
15682             
15683             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15684             
15685             this.hiddenField.dom.value =
15686                 this.hiddenValue !== undefined ? this.hiddenValue :
15687                 this.value !== undefined ? this.value : '';
15688
15689             // prevent input submission
15690             this.el.dom.removeAttribute('name');
15691             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15692              
15693              
15694         }
15695         //if(Roo.isGecko){
15696         //    this.el.dom.setAttribute('autocomplete', 'off');
15697         //}
15698         
15699         var cls = 'x-combo-list';
15700         
15701         //this.list = new Roo.Layer({
15702         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15703         //});
15704         
15705         var _this = this;
15706         
15707         (function(){
15708             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15709             _this.list.setWidth(lw);
15710         }).defer(100);
15711         
15712         this.list.on('mouseover', this.onViewOver, this);
15713         this.list.on('mousemove', this.onViewMove, this);
15714         this.list.on('scroll', this.onViewScroll, this);
15715         
15716         /*
15717         this.list.swallowEvent('mousewheel');
15718         this.assetHeight = 0;
15719
15720         if(this.title){
15721             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15722             this.assetHeight += this.header.getHeight();
15723         }
15724
15725         this.innerList = this.list.createChild({cls:cls+'-inner'});
15726         this.innerList.on('mouseover', this.onViewOver, this);
15727         this.innerList.on('mousemove', this.onViewMove, this);
15728         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15729         
15730         if(this.allowBlank && !this.pageSize && !this.disableClear){
15731             this.footer = this.list.createChild({cls:cls+'-ft'});
15732             this.pageTb = new Roo.Toolbar(this.footer);
15733            
15734         }
15735         if(this.pageSize){
15736             this.footer = this.list.createChild({cls:cls+'-ft'});
15737             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15738                     {pageSize: this.pageSize});
15739             
15740         }
15741         
15742         if (this.pageTb && this.allowBlank && !this.disableClear) {
15743             var _this = this;
15744             this.pageTb.add(new Roo.Toolbar.Fill(), {
15745                 cls: 'x-btn-icon x-btn-clear',
15746                 text: '&#160;',
15747                 handler: function()
15748                 {
15749                     _this.collapse();
15750                     _this.clearValue();
15751                     _this.onSelect(false, -1);
15752                 }
15753             });
15754         }
15755         if (this.footer) {
15756             this.assetHeight += this.footer.getHeight();
15757         }
15758         */
15759             
15760         if(!this.tpl){
15761             this.tpl = Roo.bootstrap.version == 4 ?
15762                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15763                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15764         }
15765
15766         this.view = new Roo.View(this.list, this.tpl, {
15767             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15768         });
15769         //this.view.wrapEl.setDisplayed(false);
15770         this.view.on('click', this.onViewClick, this);
15771         
15772         
15773         this.store.on('beforeload', this.onBeforeLoad, this);
15774         this.store.on('load', this.onLoad, this);
15775         this.store.on('loadexception', this.onLoadException, this);
15776         /*
15777         if(this.resizable){
15778             this.resizer = new Roo.Resizable(this.list,  {
15779                pinned:true, handles:'se'
15780             });
15781             this.resizer.on('resize', function(r, w, h){
15782                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15783                 this.listWidth = w;
15784                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15785                 this.restrictHeight();
15786             }, this);
15787             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15788         }
15789         */
15790         if(!this.editable){
15791             this.editable = true;
15792             this.setEditable(false);
15793         }
15794         
15795         /*
15796         
15797         if (typeof(this.events.add.listeners) != 'undefined') {
15798             
15799             this.addicon = this.wrap.createChild(
15800                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15801        
15802             this.addicon.on('click', function(e) {
15803                 this.fireEvent('add', this);
15804             }, this);
15805         }
15806         if (typeof(this.events.edit.listeners) != 'undefined') {
15807             
15808             this.editicon = this.wrap.createChild(
15809                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15810             if (this.addicon) {
15811                 this.editicon.setStyle('margin-left', '40px');
15812             }
15813             this.editicon.on('click', function(e) {
15814                 
15815                 // we fire even  if inothing is selected..
15816                 this.fireEvent('edit', this, this.lastData );
15817                 
15818             }, this);
15819         }
15820         */
15821         
15822         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15823             "up" : function(e){
15824                 this.inKeyMode = true;
15825                 this.selectPrev();
15826             },
15827
15828             "down" : function(e){
15829                 if(!this.isExpanded()){
15830                     this.onTriggerClick();
15831                 }else{
15832                     this.inKeyMode = true;
15833                     this.selectNext();
15834                 }
15835             },
15836
15837             "enter" : function(e){
15838 //                this.onViewClick();
15839                 //return true;
15840                 this.collapse();
15841                 
15842                 if(this.fireEvent("specialkey", this, e)){
15843                     this.onViewClick(false);
15844                 }
15845                 
15846                 return true;
15847             },
15848
15849             "esc" : function(e){
15850                 this.collapse();
15851             },
15852
15853             "tab" : function(e){
15854                 this.collapse();
15855                 
15856                 if(this.fireEvent("specialkey", this, e)){
15857                     this.onViewClick(false);
15858                 }
15859                 
15860                 return true;
15861             },
15862
15863             scope : this,
15864
15865             doRelay : function(foo, bar, hname){
15866                 if(hname == 'down' || this.scope.isExpanded()){
15867                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15868                 }
15869                 return true;
15870             },
15871
15872             forceKeyDown: true
15873         });
15874         
15875         
15876         this.queryDelay = Math.max(this.queryDelay || 10,
15877                 this.mode == 'local' ? 10 : 250);
15878         
15879         
15880         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15881         
15882         if(this.typeAhead){
15883             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15884         }
15885         if(this.editable !== false){
15886             this.inputEl().on("keyup", this.onKeyUp, this);
15887         }
15888         if(this.forceSelection){
15889             this.inputEl().on('blur', this.doForce, this);
15890         }
15891         
15892         if(this.multiple){
15893             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15894             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15895         }
15896     },
15897     
15898     initTickableEvents: function()
15899     {   
15900         this.createList();
15901         
15902         if(this.hiddenName){
15903             
15904             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15905             
15906             this.hiddenField.dom.value =
15907                 this.hiddenValue !== undefined ? this.hiddenValue :
15908                 this.value !== undefined ? this.value : '';
15909
15910             // prevent input submission
15911             this.el.dom.removeAttribute('name');
15912             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15913              
15914              
15915         }
15916         
15917 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15918         
15919         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15920         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15921         if(this.triggerList){
15922             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15923         }
15924          
15925         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15926         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15927         
15928         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15929         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15930         
15931         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15932         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15933         
15934         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15935         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15936         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15937         
15938         this.okBtn.hide();
15939         this.cancelBtn.hide();
15940         
15941         var _this = this;
15942         
15943         (function(){
15944             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15945             _this.list.setWidth(lw);
15946         }).defer(100);
15947         
15948         this.list.on('mouseover', this.onViewOver, this);
15949         this.list.on('mousemove', this.onViewMove, this);
15950         
15951         this.list.on('scroll', this.onViewScroll, this);
15952         
15953         if(!this.tpl){
15954             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15955                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15956         }
15957
15958         this.view = new Roo.View(this.list, this.tpl, {
15959             singleSelect:true,
15960             tickable:true,
15961             parent:this,
15962             store: this.store,
15963             selectedClass: this.selectedClass
15964         });
15965         
15966         //this.view.wrapEl.setDisplayed(false);
15967         this.view.on('click', this.onViewClick, this);
15968         
15969         
15970         
15971         this.store.on('beforeload', this.onBeforeLoad, this);
15972         this.store.on('load', this.onLoad, this);
15973         this.store.on('loadexception', this.onLoadException, this);
15974         
15975         if(this.editable){
15976             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15977                 "up" : function(e){
15978                     this.inKeyMode = true;
15979                     this.selectPrev();
15980                 },
15981
15982                 "down" : function(e){
15983                     this.inKeyMode = true;
15984                     this.selectNext();
15985                 },
15986
15987                 "enter" : function(e){
15988                     if(this.fireEvent("specialkey", this, e)){
15989                         this.onViewClick(false);
15990                     }
15991                     
15992                     return true;
15993                 },
15994
15995                 "esc" : function(e){
15996                     this.onTickableFooterButtonClick(e, false, false);
15997                 },
15998
15999                 "tab" : function(e){
16000                     this.fireEvent("specialkey", this, e);
16001                     
16002                     this.onTickableFooterButtonClick(e, false, false);
16003                     
16004                     return true;
16005                 },
16006
16007                 scope : this,
16008
16009                 doRelay : function(e, fn, key){
16010                     if(this.scope.isExpanded()){
16011                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16012                     }
16013                     return true;
16014                 },
16015
16016                 forceKeyDown: true
16017             });
16018         }
16019         
16020         this.queryDelay = Math.max(this.queryDelay || 10,
16021                 this.mode == 'local' ? 10 : 250);
16022         
16023         
16024         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16025         
16026         if(this.typeAhead){
16027             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16028         }
16029         
16030         if(this.editable !== false){
16031             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16032         }
16033         
16034         this.indicator = this.indicatorEl();
16035         
16036         if(this.indicator){
16037             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16038             this.indicator.hide();
16039         }
16040         
16041     },
16042
16043     onDestroy : function(){
16044         if(this.view){
16045             this.view.setStore(null);
16046             this.view.el.removeAllListeners();
16047             this.view.el.remove();
16048             this.view.purgeListeners();
16049         }
16050         if(this.list){
16051             this.list.dom.innerHTML  = '';
16052         }
16053         
16054         if(this.store){
16055             this.store.un('beforeload', this.onBeforeLoad, this);
16056             this.store.un('load', this.onLoad, this);
16057             this.store.un('loadexception', this.onLoadException, this);
16058         }
16059         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16060     },
16061
16062     // private
16063     fireKey : function(e){
16064         if(e.isNavKeyPress() && !this.list.isVisible()){
16065             this.fireEvent("specialkey", this, e);
16066         }
16067     },
16068
16069     // private
16070     onResize: function(w, h)
16071     {
16072         
16073         
16074 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16075 //        
16076 //        if(typeof w != 'number'){
16077 //            // we do not handle it!?!?
16078 //            return;
16079 //        }
16080 //        var tw = this.trigger.getWidth();
16081 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16082 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16083 //        var x = w - tw;
16084 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16085 //            
16086 //        //this.trigger.setStyle('left', x+'px');
16087 //        
16088 //        if(this.list && this.listWidth === undefined){
16089 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16090 //            this.list.setWidth(lw);
16091 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16092 //        }
16093         
16094     
16095         
16096     },
16097
16098     /**
16099      * Allow or prevent the user from directly editing the field text.  If false is passed,
16100      * the user will only be able to select from the items defined in the dropdown list.  This method
16101      * is the runtime equivalent of setting the 'editable' config option at config time.
16102      * @param {Boolean} value True to allow the user to directly edit the field text
16103      */
16104     setEditable : function(value){
16105         if(value == this.editable){
16106             return;
16107         }
16108         this.editable = value;
16109         if(!value){
16110             this.inputEl().dom.setAttribute('readOnly', true);
16111             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16112             this.inputEl().addClass('x-combo-noedit');
16113         }else{
16114             this.inputEl().dom.setAttribute('readOnly', false);
16115             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16116             this.inputEl().removeClass('x-combo-noedit');
16117         }
16118     },
16119
16120     // private
16121     
16122     onBeforeLoad : function(combo,opts){
16123         if(!this.hasFocus){
16124             return;
16125         }
16126          if (!opts.add) {
16127             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16128          }
16129         this.restrictHeight();
16130         this.selectedIndex = -1;
16131     },
16132
16133     // private
16134     onLoad : function(){
16135         
16136         this.hasQuery = false;
16137         
16138         if(!this.hasFocus){
16139             return;
16140         }
16141         
16142         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16143             this.loading.hide();
16144         }
16145         
16146         if(this.store.getCount() > 0){
16147             
16148             this.expand();
16149             this.restrictHeight();
16150             if(this.lastQuery == this.allQuery){
16151                 if(this.editable && !this.tickable){
16152                     this.inputEl().dom.select();
16153                 }
16154                 
16155                 if(
16156                     !this.selectByValue(this.value, true) &&
16157                     this.autoFocus && 
16158                     (
16159                         !this.store.lastOptions ||
16160                         typeof(this.store.lastOptions.add) == 'undefined' || 
16161                         this.store.lastOptions.add != true
16162                     )
16163                 ){
16164                     this.select(0, true);
16165                 }
16166             }else{
16167                 if(this.autoFocus){
16168                     this.selectNext();
16169                 }
16170                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16171                     this.taTask.delay(this.typeAheadDelay);
16172                 }
16173             }
16174         }else{
16175             this.onEmptyResults();
16176         }
16177         
16178         //this.el.focus();
16179     },
16180     // private
16181     onLoadException : function()
16182     {
16183         this.hasQuery = false;
16184         
16185         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16186             this.loading.hide();
16187         }
16188         
16189         if(this.tickable && this.editable){
16190             return;
16191         }
16192         
16193         this.collapse();
16194         // only causes errors at present
16195         //Roo.log(this.store.reader.jsonData);
16196         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16197             // fixme
16198             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16199         //}
16200         
16201         
16202     },
16203     // private
16204     onTypeAhead : function(){
16205         if(this.store.getCount() > 0){
16206             var r = this.store.getAt(0);
16207             var newValue = r.data[this.displayField];
16208             var len = newValue.length;
16209             var selStart = this.getRawValue().length;
16210             
16211             if(selStart != len){
16212                 this.setRawValue(newValue);
16213                 this.selectText(selStart, newValue.length);
16214             }
16215         }
16216     },
16217
16218     // private
16219     onSelect : function(record, index){
16220         
16221         if(this.fireEvent('beforeselect', this, record, index) !== false){
16222         
16223             this.setFromData(index > -1 ? record.data : false);
16224             
16225             this.collapse();
16226             this.fireEvent('select', this, record, index);
16227         }
16228     },
16229
16230     /**
16231      * Returns the currently selected field value or empty string if no value is set.
16232      * @return {String} value The selected value
16233      */
16234     getValue : function()
16235     {
16236         if(Roo.isIOS && this.useNativeIOS){
16237             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16238         }
16239         
16240         if(this.multiple){
16241             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16242         }
16243         
16244         if(this.valueField){
16245             return typeof this.value != 'undefined' ? this.value : '';
16246         }else{
16247             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16248         }
16249     },
16250     
16251     getRawValue : function()
16252     {
16253         if(Roo.isIOS && this.useNativeIOS){
16254             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16255         }
16256         
16257         var v = this.inputEl().getValue();
16258         
16259         return v;
16260     },
16261
16262     /**
16263      * Clears any text/value currently set in the field
16264      */
16265     clearValue : function(){
16266         
16267         if(this.hiddenField){
16268             this.hiddenField.dom.value = '';
16269         }
16270         this.value = '';
16271         this.setRawValue('');
16272         this.lastSelectionText = '';
16273         this.lastData = false;
16274         
16275         var close = this.closeTriggerEl();
16276         
16277         if(close){
16278             close.hide();
16279         }
16280         
16281         this.validate();
16282         
16283     },
16284
16285     /**
16286      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16287      * will be displayed in the field.  If the value does not match the data value of an existing item,
16288      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16289      * Otherwise the field will be blank (although the value will still be set).
16290      * @param {String} value The value to match
16291      */
16292     setValue : function(v)
16293     {
16294         if(Roo.isIOS && this.useNativeIOS){
16295             this.setIOSValue(v);
16296             return;
16297         }
16298         
16299         if(this.multiple){
16300             this.syncValue();
16301             return;
16302         }
16303         
16304         var text = v;
16305         if(this.valueField){
16306             var r = this.findRecord(this.valueField, v);
16307             if(r){
16308                 text = r.data[this.displayField];
16309             }else if(this.valueNotFoundText !== undefined){
16310                 text = this.valueNotFoundText;
16311             }
16312         }
16313         this.lastSelectionText = text;
16314         if(this.hiddenField){
16315             this.hiddenField.dom.value = v;
16316         }
16317         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16318         this.value = v;
16319         
16320         var close = this.closeTriggerEl();
16321         
16322         if(close){
16323             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16324         }
16325         
16326         this.validate();
16327     },
16328     /**
16329      * @property {Object} the last set data for the element
16330      */
16331     
16332     lastData : false,
16333     /**
16334      * Sets the value of the field based on a object which is related to the record format for the store.
16335      * @param {Object} value the value to set as. or false on reset?
16336      */
16337     setFromData : function(o){
16338         
16339         if(this.multiple){
16340             this.addItem(o);
16341             return;
16342         }
16343             
16344         var dv = ''; // display value
16345         var vv = ''; // value value..
16346         this.lastData = o;
16347         if (this.displayField) {
16348             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16349         } else {
16350             // this is an error condition!!!
16351             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16352         }
16353         
16354         if(this.valueField){
16355             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16356         }
16357         
16358         var close = this.closeTriggerEl();
16359         
16360         if(close){
16361             if(dv.length || vv * 1 > 0){
16362                 close.show() ;
16363                 this.blockFocus=true;
16364             } else {
16365                 close.hide();
16366             }             
16367         }
16368         
16369         if(this.hiddenField){
16370             this.hiddenField.dom.value = vv;
16371             
16372             this.lastSelectionText = dv;
16373             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16374             this.value = vv;
16375             return;
16376         }
16377         // no hidden field.. - we store the value in 'value', but still display
16378         // display field!!!!
16379         this.lastSelectionText = dv;
16380         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16381         this.value = vv;
16382         
16383         
16384         
16385     },
16386     // private
16387     reset : function(){
16388         // overridden so that last data is reset..
16389         
16390         if(this.multiple){
16391             this.clearItem();
16392             return;
16393         }
16394         
16395         this.setValue(this.originalValue);
16396         //this.clearInvalid();
16397         this.lastData = false;
16398         if (this.view) {
16399             this.view.clearSelections();
16400         }
16401         
16402         this.validate();
16403     },
16404     // private
16405     findRecord : function(prop, value){
16406         var record;
16407         if(this.store.getCount() > 0){
16408             this.store.each(function(r){
16409                 if(r.data[prop] == value){
16410                     record = r;
16411                     return false;
16412                 }
16413                 return true;
16414             });
16415         }
16416         return record;
16417     },
16418     
16419     getName: function()
16420     {
16421         // returns hidden if it's set..
16422         if (!this.rendered) {return ''};
16423         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16424         
16425     },
16426     // private
16427     onViewMove : function(e, t){
16428         this.inKeyMode = false;
16429     },
16430
16431     // private
16432     onViewOver : function(e, t){
16433         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16434             return;
16435         }
16436         var item = this.view.findItemFromChild(t);
16437         
16438         if(item){
16439             var index = this.view.indexOf(item);
16440             this.select(index, false);
16441         }
16442     },
16443
16444     // private
16445     onViewClick : function(view, doFocus, el, e)
16446     {
16447         var index = this.view.getSelectedIndexes()[0];
16448         
16449         var r = this.store.getAt(index);
16450         
16451         if(this.tickable){
16452             
16453             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16454                 return;
16455             }
16456             
16457             var rm = false;
16458             var _this = this;
16459             
16460             Roo.each(this.tickItems, function(v,k){
16461                 
16462                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16463                     Roo.log(v);
16464                     _this.tickItems.splice(k, 1);
16465                     
16466                     if(typeof(e) == 'undefined' && view == false){
16467                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16468                     }
16469                     
16470                     rm = true;
16471                     return;
16472                 }
16473             });
16474             
16475             if(rm){
16476                 return;
16477             }
16478             
16479             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16480                 this.tickItems.push(r.data);
16481             }
16482             
16483             if(typeof(e) == 'undefined' && view == false){
16484                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16485             }
16486                     
16487             return;
16488         }
16489         
16490         if(r){
16491             this.onSelect(r, index);
16492         }
16493         if(doFocus !== false && !this.blockFocus){
16494             this.inputEl().focus();
16495         }
16496     },
16497
16498     // private
16499     restrictHeight : function(){
16500         //this.innerList.dom.style.height = '';
16501         //var inner = this.innerList.dom;
16502         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16503         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16504         //this.list.beginUpdate();
16505         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16506         this.list.alignTo(this.inputEl(), this.listAlign);
16507         this.list.alignTo(this.inputEl(), this.listAlign);
16508         //this.list.endUpdate();
16509     },
16510
16511     // private
16512     onEmptyResults : function(){
16513         
16514         if(this.tickable && this.editable){
16515             this.hasFocus = false;
16516             this.restrictHeight();
16517             return;
16518         }
16519         
16520         this.collapse();
16521     },
16522
16523     /**
16524      * Returns true if the dropdown list is expanded, else false.
16525      */
16526     isExpanded : function(){
16527         return this.list.isVisible();
16528     },
16529
16530     /**
16531      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16532      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16533      * @param {String} value The data value of the item to select
16534      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16535      * selected item if it is not currently in view (defaults to true)
16536      * @return {Boolean} True if the value matched an item in the list, else false
16537      */
16538     selectByValue : function(v, scrollIntoView){
16539         if(v !== undefined && v !== null){
16540             var r = this.findRecord(this.valueField || this.displayField, v);
16541             if(r){
16542                 this.select(this.store.indexOf(r), scrollIntoView);
16543                 return true;
16544             }
16545         }
16546         return false;
16547     },
16548
16549     /**
16550      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16551      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16552      * @param {Number} index The zero-based index of the list item to select
16553      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16554      * selected item if it is not currently in view (defaults to true)
16555      */
16556     select : function(index, scrollIntoView){
16557         this.selectedIndex = index;
16558         this.view.select(index);
16559         if(scrollIntoView !== false){
16560             var el = this.view.getNode(index);
16561             /*
16562              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16563              */
16564             if(el){
16565                 this.list.scrollChildIntoView(el, false);
16566             }
16567         }
16568     },
16569
16570     // private
16571     selectNext : function(){
16572         var ct = this.store.getCount();
16573         if(ct > 0){
16574             if(this.selectedIndex == -1){
16575                 this.select(0);
16576             }else if(this.selectedIndex < ct-1){
16577                 this.select(this.selectedIndex+1);
16578             }
16579         }
16580     },
16581
16582     // private
16583     selectPrev : function(){
16584         var ct = this.store.getCount();
16585         if(ct > 0){
16586             if(this.selectedIndex == -1){
16587                 this.select(0);
16588             }else if(this.selectedIndex != 0){
16589                 this.select(this.selectedIndex-1);
16590             }
16591         }
16592     },
16593
16594     // private
16595     onKeyUp : function(e){
16596         if(this.editable !== false && !e.isSpecialKey()){
16597             this.lastKey = e.getKey();
16598             this.dqTask.delay(this.queryDelay);
16599         }
16600     },
16601
16602     // private
16603     validateBlur : function(){
16604         return !this.list || !this.list.isVisible();   
16605     },
16606
16607     // private
16608     initQuery : function(){
16609         
16610         var v = this.getRawValue();
16611         
16612         if(this.tickable && this.editable){
16613             v = this.tickableInputEl().getValue();
16614         }
16615         
16616         this.doQuery(v);
16617     },
16618
16619     // private
16620     doForce : function(){
16621         if(this.inputEl().dom.value.length > 0){
16622             this.inputEl().dom.value =
16623                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16624              
16625         }
16626     },
16627
16628     /**
16629      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16630      * query allowing the query action to be canceled if needed.
16631      * @param {String} query The SQL query to execute
16632      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16633      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16634      * saved in the current store (defaults to false)
16635      */
16636     doQuery : function(q, forceAll){
16637         
16638         if(q === undefined || q === null){
16639             q = '';
16640         }
16641         var qe = {
16642             query: q,
16643             forceAll: forceAll,
16644             combo: this,
16645             cancel:false
16646         };
16647         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16648             return false;
16649         }
16650         q = qe.query;
16651         
16652         forceAll = qe.forceAll;
16653         if(forceAll === true || (q.length >= this.minChars)){
16654             
16655             this.hasQuery = true;
16656             
16657             if(this.lastQuery != q || this.alwaysQuery){
16658                 this.lastQuery = q;
16659                 if(this.mode == 'local'){
16660                     this.selectedIndex = -1;
16661                     if(forceAll){
16662                         this.store.clearFilter();
16663                     }else{
16664                         
16665                         if(this.specialFilter){
16666                             this.fireEvent('specialfilter', this);
16667                             this.onLoad();
16668                             return;
16669                         }
16670                         
16671                         this.store.filter(this.displayField, q);
16672                     }
16673                     
16674                     this.store.fireEvent("datachanged", this.store);
16675                     
16676                     this.onLoad();
16677                     
16678                     
16679                 }else{
16680                     
16681                     this.store.baseParams[this.queryParam] = q;
16682                     
16683                     var options = {params : this.getParams(q)};
16684                     
16685                     if(this.loadNext){
16686                         options.add = true;
16687                         options.params.start = this.page * this.pageSize;
16688                     }
16689                     
16690                     this.store.load(options);
16691                     
16692                     /*
16693                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16694                      *  we should expand the list on onLoad
16695                      *  so command out it
16696                      */
16697 //                    this.expand();
16698                 }
16699             }else{
16700                 this.selectedIndex = -1;
16701                 this.onLoad();   
16702             }
16703         }
16704         
16705         this.loadNext = false;
16706     },
16707     
16708     // private
16709     getParams : function(q){
16710         var p = {};
16711         //p[this.queryParam] = q;
16712         
16713         if(this.pageSize){
16714             p.start = 0;
16715             p.limit = this.pageSize;
16716         }
16717         return p;
16718     },
16719
16720     /**
16721      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16722      */
16723     collapse : function(){
16724         if(!this.isExpanded()){
16725             return;
16726         }
16727         
16728         this.list.hide();
16729         
16730         this.hasFocus = false;
16731         
16732         if(this.tickable){
16733             this.okBtn.hide();
16734             this.cancelBtn.hide();
16735             this.trigger.show();
16736             
16737             if(this.editable){
16738                 this.tickableInputEl().dom.value = '';
16739                 this.tickableInputEl().blur();
16740             }
16741             
16742         }
16743         
16744         Roo.get(document).un('mousedown', this.collapseIf, this);
16745         Roo.get(document).un('mousewheel', this.collapseIf, this);
16746         if (!this.editable) {
16747             Roo.get(document).un('keydown', this.listKeyPress, this);
16748         }
16749         this.fireEvent('collapse', this);
16750         
16751         this.validate();
16752     },
16753
16754     // private
16755     collapseIf : function(e){
16756         var in_combo  = e.within(this.el);
16757         var in_list =  e.within(this.list);
16758         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16759         
16760         if (in_combo || in_list || is_list) {
16761             //e.stopPropagation();
16762             return;
16763         }
16764         
16765         if(this.tickable){
16766             this.onTickableFooterButtonClick(e, false, false);
16767         }
16768
16769         this.collapse();
16770         
16771     },
16772
16773     /**
16774      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16775      */
16776     expand : function(){
16777        
16778         if(this.isExpanded() || !this.hasFocus){
16779             return;
16780         }
16781         
16782         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16783         this.list.setWidth(lw);
16784         
16785         Roo.log('expand');
16786         
16787         this.list.show();
16788         
16789         this.restrictHeight();
16790         
16791         if(this.tickable){
16792             
16793             this.tickItems = Roo.apply([], this.item);
16794             
16795             this.okBtn.show();
16796             this.cancelBtn.show();
16797             this.trigger.hide();
16798             
16799             if(this.editable){
16800                 this.tickableInputEl().focus();
16801             }
16802             
16803         }
16804         
16805         Roo.get(document).on('mousedown', this.collapseIf, this);
16806         Roo.get(document).on('mousewheel', this.collapseIf, this);
16807         if (!this.editable) {
16808             Roo.get(document).on('keydown', this.listKeyPress, this);
16809         }
16810         
16811         this.fireEvent('expand', this);
16812     },
16813
16814     // private
16815     // Implements the default empty TriggerField.onTriggerClick function
16816     onTriggerClick : function(e)
16817     {
16818         Roo.log('trigger click');
16819         
16820         if(this.disabled || !this.triggerList){
16821             return;
16822         }
16823         
16824         this.page = 0;
16825         this.loadNext = false;
16826         
16827         if(this.isExpanded()){
16828             this.collapse();
16829             if (!this.blockFocus) {
16830                 this.inputEl().focus();
16831             }
16832             
16833         }else {
16834             this.hasFocus = true;
16835             if(this.triggerAction == 'all') {
16836                 this.doQuery(this.allQuery, true);
16837             } else {
16838                 this.doQuery(this.getRawValue());
16839             }
16840             if (!this.blockFocus) {
16841                 this.inputEl().focus();
16842             }
16843         }
16844     },
16845     
16846     onTickableTriggerClick : function(e)
16847     {
16848         if(this.disabled){
16849             return;
16850         }
16851         
16852         this.page = 0;
16853         this.loadNext = false;
16854         this.hasFocus = true;
16855         
16856         if(this.triggerAction == 'all') {
16857             this.doQuery(this.allQuery, true);
16858         } else {
16859             this.doQuery(this.getRawValue());
16860         }
16861     },
16862     
16863     onSearchFieldClick : function(e)
16864     {
16865         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16866             this.onTickableFooterButtonClick(e, false, false);
16867             return;
16868         }
16869         
16870         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16871             return;
16872         }
16873         
16874         this.page = 0;
16875         this.loadNext = false;
16876         this.hasFocus = true;
16877         
16878         if(this.triggerAction == 'all') {
16879             this.doQuery(this.allQuery, true);
16880         } else {
16881             this.doQuery(this.getRawValue());
16882         }
16883     },
16884     
16885     listKeyPress : function(e)
16886     {
16887         //Roo.log('listkeypress');
16888         // scroll to first matching element based on key pres..
16889         if (e.isSpecialKey()) {
16890             return false;
16891         }
16892         var k = String.fromCharCode(e.getKey()).toUpperCase();
16893         //Roo.log(k);
16894         var match  = false;
16895         var csel = this.view.getSelectedNodes();
16896         var cselitem = false;
16897         if (csel.length) {
16898             var ix = this.view.indexOf(csel[0]);
16899             cselitem  = this.store.getAt(ix);
16900             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16901                 cselitem = false;
16902             }
16903             
16904         }
16905         
16906         this.store.each(function(v) { 
16907             if (cselitem) {
16908                 // start at existing selection.
16909                 if (cselitem.id == v.id) {
16910                     cselitem = false;
16911                 }
16912                 return true;
16913             }
16914                 
16915             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16916                 match = this.store.indexOf(v);
16917                 return false;
16918             }
16919             return true;
16920         }, this);
16921         
16922         if (match === false) {
16923             return true; // no more action?
16924         }
16925         // scroll to?
16926         this.view.select(match);
16927         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16928         sn.scrollIntoView(sn.dom.parentNode, false);
16929     },
16930     
16931     onViewScroll : function(e, t){
16932         
16933         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){
16934             return;
16935         }
16936         
16937         this.hasQuery = true;
16938         
16939         this.loading = this.list.select('.loading', true).first();
16940         
16941         if(this.loading === null){
16942             this.list.createChild({
16943                 tag: 'div',
16944                 cls: 'loading roo-select2-more-results roo-select2-active',
16945                 html: 'Loading more results...'
16946             });
16947             
16948             this.loading = this.list.select('.loading', true).first();
16949             
16950             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16951             
16952             this.loading.hide();
16953         }
16954         
16955         this.loading.show();
16956         
16957         var _combo = this;
16958         
16959         this.page++;
16960         this.loadNext = true;
16961         
16962         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16963         
16964         return;
16965     },
16966     
16967     addItem : function(o)
16968     {   
16969         var dv = ''; // display value
16970         
16971         if (this.displayField) {
16972             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16973         } else {
16974             // this is an error condition!!!
16975             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16976         }
16977         
16978         if(!dv.length){
16979             return;
16980         }
16981         
16982         var choice = this.choices.createChild({
16983             tag: 'li',
16984             cls: 'roo-select2-search-choice',
16985             cn: [
16986                 {
16987                     tag: 'div',
16988                     html: dv
16989                 },
16990                 {
16991                     tag: 'a',
16992                     href: '#',
16993                     cls: 'roo-select2-search-choice-close fa fa-times',
16994                     tabindex: '-1'
16995                 }
16996             ]
16997             
16998         }, this.searchField);
16999         
17000         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17001         
17002         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17003         
17004         this.item.push(o);
17005         
17006         this.lastData = o;
17007         
17008         this.syncValue();
17009         
17010         this.inputEl().dom.value = '';
17011         
17012         this.validate();
17013     },
17014     
17015     onRemoveItem : function(e, _self, o)
17016     {
17017         e.preventDefault();
17018         
17019         this.lastItem = Roo.apply([], this.item);
17020         
17021         var index = this.item.indexOf(o.data) * 1;
17022         
17023         if( index < 0){
17024             Roo.log('not this item?!');
17025             return;
17026         }
17027         
17028         this.item.splice(index, 1);
17029         o.item.remove();
17030         
17031         this.syncValue();
17032         
17033         this.fireEvent('remove', this, e);
17034         
17035         this.validate();
17036         
17037     },
17038     
17039     syncValue : function()
17040     {
17041         if(!this.item.length){
17042             this.clearValue();
17043             return;
17044         }
17045             
17046         var value = [];
17047         var _this = this;
17048         Roo.each(this.item, function(i){
17049             if(_this.valueField){
17050                 value.push(i[_this.valueField]);
17051                 return;
17052             }
17053
17054             value.push(i);
17055         });
17056
17057         this.value = value.join(',');
17058
17059         if(this.hiddenField){
17060             this.hiddenField.dom.value = this.value;
17061         }
17062         
17063         this.store.fireEvent("datachanged", this.store);
17064         
17065         this.validate();
17066     },
17067     
17068     clearItem : function()
17069     {
17070         if(!this.multiple){
17071             return;
17072         }
17073         
17074         this.item = [];
17075         
17076         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17077            c.remove();
17078         });
17079         
17080         this.syncValue();
17081         
17082         this.validate();
17083         
17084         if(this.tickable && !Roo.isTouch){
17085             this.view.refresh();
17086         }
17087     },
17088     
17089     inputEl: function ()
17090     {
17091         if(Roo.isIOS && this.useNativeIOS){
17092             return this.el.select('select.roo-ios-select', true).first();
17093         }
17094         
17095         if(Roo.isTouch && this.mobileTouchView){
17096             return this.el.select('input.form-control',true).first();
17097         }
17098         
17099         if(this.tickable){
17100             return this.searchField;
17101         }
17102         
17103         return this.el.select('input.form-control',true).first();
17104     },
17105     
17106     onTickableFooterButtonClick : function(e, btn, el)
17107     {
17108         e.preventDefault();
17109         
17110         this.lastItem = Roo.apply([], this.item);
17111         
17112         if(btn && btn.name == 'cancel'){
17113             this.tickItems = Roo.apply([], this.item);
17114             this.collapse();
17115             return;
17116         }
17117         
17118         this.clearItem();
17119         
17120         var _this = this;
17121         
17122         Roo.each(this.tickItems, function(o){
17123             _this.addItem(o);
17124         });
17125         
17126         this.collapse();
17127         
17128     },
17129     
17130     validate : function()
17131     {
17132         if(this.getVisibilityEl().hasClass('hidden')){
17133             return true;
17134         }
17135         
17136         var v = this.getRawValue();
17137         
17138         if(this.multiple){
17139             v = this.getValue();
17140         }
17141         
17142         if(this.disabled || this.allowBlank || v.length){
17143             this.markValid();
17144             return true;
17145         }
17146         
17147         this.markInvalid();
17148         return false;
17149     },
17150     
17151     tickableInputEl : function()
17152     {
17153         if(!this.tickable || !this.editable){
17154             return this.inputEl();
17155         }
17156         
17157         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17158     },
17159     
17160     
17161     getAutoCreateTouchView : function()
17162     {
17163         var id = Roo.id();
17164         
17165         var cfg = {
17166             cls: 'form-group' //input-group
17167         };
17168         
17169         var input =  {
17170             tag: 'input',
17171             id : id,
17172             type : this.inputType,
17173             cls : 'form-control x-combo-noedit',
17174             autocomplete: 'new-password',
17175             placeholder : this.placeholder || '',
17176             readonly : true
17177         };
17178         
17179         if (this.name) {
17180             input.name = this.name;
17181         }
17182         
17183         if (this.size) {
17184             input.cls += ' input-' + this.size;
17185         }
17186         
17187         if (this.disabled) {
17188             input.disabled = true;
17189         }
17190         
17191         var inputblock = {
17192             cls : 'roo-combobox-wrap',
17193             cn : [
17194                 input
17195             ]
17196         };
17197         
17198         if(this.before){
17199             inputblock.cls += ' input-group';
17200             
17201             inputblock.cn.unshift({
17202                 tag :'span',
17203                 cls : 'input-group-addon input-group-prepend input-group-text',
17204                 html : this.before
17205             });
17206         }
17207         
17208         if(this.removable && !this.multiple){
17209             inputblock.cls += ' roo-removable';
17210             
17211             inputblock.cn.push({
17212                 tag: 'button',
17213                 html : 'x',
17214                 cls : 'roo-combo-removable-btn close'
17215             });
17216         }
17217
17218         if(this.hasFeedback && !this.allowBlank){
17219             
17220             inputblock.cls += ' has-feedback';
17221             
17222             inputblock.cn.push({
17223                 tag: 'span',
17224                 cls: 'glyphicon form-control-feedback'
17225             });
17226             
17227         }
17228         
17229         if (this.after) {
17230             
17231             inputblock.cls += (this.before) ? '' : ' input-group';
17232             
17233             inputblock.cn.push({
17234                 tag :'span',
17235                 cls : 'input-group-addon input-group-append input-group-text',
17236                 html : this.after
17237             });
17238         }
17239
17240         
17241         var ibwrap = inputblock;
17242         
17243         if(this.multiple){
17244             ibwrap = {
17245                 tag: 'ul',
17246                 cls: 'roo-select2-choices',
17247                 cn:[
17248                     {
17249                         tag: 'li',
17250                         cls: 'roo-select2-search-field',
17251                         cn: [
17252
17253                             inputblock
17254                         ]
17255                     }
17256                 ]
17257             };
17258         
17259             
17260         }
17261         
17262         var combobox = {
17263             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17264             cn: [
17265                 {
17266                     tag: 'input',
17267                     type : 'hidden',
17268                     cls: 'form-hidden-field'
17269                 },
17270                 ibwrap
17271             ]
17272         };
17273         
17274         if(!this.multiple && this.showToggleBtn){
17275             
17276             var caret = {
17277                 cls: 'caret'
17278             };
17279             
17280             if (this.caret != false) {
17281                 caret = {
17282                      tag: 'i',
17283                      cls: 'fa fa-' + this.caret
17284                 };
17285                 
17286             }
17287             
17288             combobox.cn.push({
17289                 tag :'span',
17290                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17291                 cn : [
17292                     Roo.bootstrap.version == 3 ? caret : '',
17293                     {
17294                         tag: 'span',
17295                         cls: 'combobox-clear',
17296                         cn  : [
17297                             {
17298                                 tag : 'i',
17299                                 cls: 'icon-remove'
17300                             }
17301                         ]
17302                     }
17303                 ]
17304
17305             })
17306         }
17307         
17308         if(this.multiple){
17309             combobox.cls += ' roo-select2-container-multi';
17310         }
17311         
17312         var align = this.labelAlign || this.parentLabelAlign();
17313         
17314         if (align ==='left' && this.fieldLabel.length) {
17315
17316             cfg.cn = [
17317                 {
17318                    tag : 'i',
17319                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17320                    tooltip : 'This field is required'
17321                 },
17322                 {
17323                     tag: 'label',
17324                     cls : 'control-label col-form-label',
17325                     html : this.fieldLabel
17326
17327                 },
17328                 {
17329                     cls : 'roo-combobox-wrap ', 
17330                     cn: [
17331                         combobox
17332                     ]
17333                 }
17334             ];
17335             
17336             var labelCfg = cfg.cn[1];
17337             var contentCfg = cfg.cn[2];
17338             
17339
17340             if(this.indicatorpos == 'right'){
17341                 cfg.cn = [
17342                     {
17343                         tag: 'label',
17344                         'for' :  id,
17345                         cls : 'control-label col-form-label',
17346                         cn : [
17347                             {
17348                                 tag : 'span',
17349                                 html : this.fieldLabel
17350                             },
17351                             {
17352                                 tag : 'i',
17353                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17354                                 tooltip : 'This field is required'
17355                             }
17356                         ]
17357                     },
17358                     {
17359                         cls : "roo-combobox-wrap ",
17360                         cn: [
17361                             combobox
17362                         ]
17363                     }
17364
17365                 ];
17366                 
17367                 labelCfg = cfg.cn[0];
17368                 contentCfg = cfg.cn[1];
17369             }
17370             
17371            
17372             
17373             if(this.labelWidth > 12){
17374                 labelCfg.style = "width: " + this.labelWidth + 'px';
17375             }
17376            
17377             if(this.labelWidth < 13 && this.labelmd == 0){
17378                 this.labelmd = this.labelWidth;
17379             }
17380             
17381             if(this.labellg > 0){
17382                 labelCfg.cls += ' col-lg-' + this.labellg;
17383                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17384             }
17385             
17386             if(this.labelmd > 0){
17387                 labelCfg.cls += ' col-md-' + this.labelmd;
17388                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17389             }
17390             
17391             if(this.labelsm > 0){
17392                 labelCfg.cls += ' col-sm-' + this.labelsm;
17393                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17394             }
17395             
17396             if(this.labelxs > 0){
17397                 labelCfg.cls += ' col-xs-' + this.labelxs;
17398                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17399             }
17400                 
17401                 
17402         } else if ( this.fieldLabel.length) {
17403             cfg.cn = [
17404                 {
17405                    tag : 'i',
17406                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17407                    tooltip : 'This field is required'
17408                 },
17409                 {
17410                     tag: 'label',
17411                     cls : 'control-label',
17412                     html : this.fieldLabel
17413
17414                 },
17415                 {
17416                     cls : '', 
17417                     cn: [
17418                         combobox
17419                     ]
17420                 }
17421             ];
17422             
17423             if(this.indicatorpos == 'right'){
17424                 cfg.cn = [
17425                     {
17426                         tag: 'label',
17427                         cls : 'control-label',
17428                         html : this.fieldLabel,
17429                         cn : [
17430                             {
17431                                tag : 'i',
17432                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17433                                tooltip : 'This field is required'
17434                             }
17435                         ]
17436                     },
17437                     {
17438                         cls : '', 
17439                         cn: [
17440                             combobox
17441                         ]
17442                     }
17443                 ];
17444             }
17445         } else {
17446             cfg.cn = combobox;    
17447         }
17448         
17449         
17450         var settings = this;
17451         
17452         ['xs','sm','md','lg'].map(function(size){
17453             if (settings[size]) {
17454                 cfg.cls += ' col-' + size + '-' + settings[size];
17455             }
17456         });
17457         
17458         return cfg;
17459     },
17460     
17461     initTouchView : function()
17462     {
17463         this.renderTouchView();
17464         
17465         this.touchViewEl.on('scroll', function(){
17466             this.el.dom.scrollTop = 0;
17467         }, this);
17468         
17469         this.originalValue = this.getValue();
17470         
17471         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17472         
17473         this.inputEl().on("click", this.showTouchView, this);
17474         if (this.triggerEl) {
17475             this.triggerEl.on("click", this.showTouchView, this);
17476         }
17477         
17478         
17479         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17480         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17481         
17482         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17483         
17484         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17485         this.store.on('load', this.onTouchViewLoad, this);
17486         this.store.on('loadexception', this.onTouchViewLoadException, this);
17487         
17488         if(this.hiddenName){
17489             
17490             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17491             
17492             this.hiddenField.dom.value =
17493                 this.hiddenValue !== undefined ? this.hiddenValue :
17494                 this.value !== undefined ? this.value : '';
17495         
17496             this.el.dom.removeAttribute('name');
17497             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17498         }
17499         
17500         if(this.multiple){
17501             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17502             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17503         }
17504         
17505         if(this.removable && !this.multiple){
17506             var close = this.closeTriggerEl();
17507             if(close){
17508                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17509                 close.on('click', this.removeBtnClick, this, close);
17510             }
17511         }
17512         /*
17513          * fix the bug in Safari iOS8
17514          */
17515         this.inputEl().on("focus", function(e){
17516             document.activeElement.blur();
17517         }, this);
17518         
17519         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17520         
17521         return;
17522         
17523         
17524     },
17525     
17526     renderTouchView : function()
17527     {
17528         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17529         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17530         
17531         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17532         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17533         
17534         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17535         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17536         this.touchViewBodyEl.setStyle('overflow', 'auto');
17537         
17538         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17539         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17540         
17541         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17542         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17543         
17544     },
17545     
17546     showTouchView : function()
17547     {
17548         if(this.disabled){
17549             return;
17550         }
17551         
17552         this.touchViewHeaderEl.hide();
17553
17554         if(this.modalTitle.length){
17555             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17556             this.touchViewHeaderEl.show();
17557         }
17558
17559         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17560         this.touchViewEl.show();
17561
17562         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17563         
17564         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17565         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17566
17567         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17568
17569         if(this.modalTitle.length){
17570             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17571         }
17572         
17573         this.touchViewBodyEl.setHeight(bodyHeight);
17574
17575         if(this.animate){
17576             var _this = this;
17577             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17578         }else{
17579             this.touchViewEl.addClass(['in','show']);
17580         }
17581         
17582         if(this._touchViewMask){
17583             Roo.get(document.body).addClass("x-body-masked");
17584             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17585             this._touchViewMask.setStyle('z-index', 10000);
17586             this._touchViewMask.addClass('show');
17587         }
17588         
17589         this.doTouchViewQuery();
17590         
17591     },
17592     
17593     hideTouchView : function()
17594     {
17595         this.touchViewEl.removeClass(['in','show']);
17596
17597         if(this.animate){
17598             var _this = this;
17599             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17600         }else{
17601             this.touchViewEl.setStyle('display', 'none');
17602         }
17603         
17604         if(this._touchViewMask){
17605             this._touchViewMask.removeClass('show');
17606             Roo.get(document.body).removeClass("x-body-masked");
17607         }
17608     },
17609     
17610     setTouchViewValue : function()
17611     {
17612         if(this.multiple){
17613             this.clearItem();
17614         
17615             var _this = this;
17616
17617             Roo.each(this.tickItems, function(o){
17618                 this.addItem(o);
17619             }, this);
17620         }
17621         
17622         this.hideTouchView();
17623     },
17624     
17625     doTouchViewQuery : function()
17626     {
17627         var qe = {
17628             query: '',
17629             forceAll: true,
17630             combo: this,
17631             cancel:false
17632         };
17633         
17634         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17635             return false;
17636         }
17637         
17638         if(!this.alwaysQuery || this.mode == 'local'){
17639             this.onTouchViewLoad();
17640             return;
17641         }
17642         
17643         this.store.load();
17644     },
17645     
17646     onTouchViewBeforeLoad : function(combo,opts)
17647     {
17648         return;
17649     },
17650
17651     // private
17652     onTouchViewLoad : function()
17653     {
17654         if(this.store.getCount() < 1){
17655             this.onTouchViewEmptyResults();
17656             return;
17657         }
17658         
17659         this.clearTouchView();
17660         
17661         var rawValue = this.getRawValue();
17662         
17663         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17664         
17665         this.tickItems = [];
17666         
17667         this.store.data.each(function(d, rowIndex){
17668             var row = this.touchViewListGroup.createChild(template);
17669             
17670             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17671                 row.addClass(d.data.cls);
17672             }
17673             
17674             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17675                 var cfg = {
17676                     data : d.data,
17677                     html : d.data[this.displayField]
17678                 };
17679                 
17680                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17681                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17682                 }
17683             }
17684             row.removeClass('selected');
17685             if(!this.multiple && this.valueField &&
17686                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17687             {
17688                 // radio buttons..
17689                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17690                 row.addClass('selected');
17691             }
17692             
17693             if(this.multiple && this.valueField &&
17694                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17695             {
17696                 
17697                 // checkboxes...
17698                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17699                 this.tickItems.push(d.data);
17700             }
17701             
17702             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17703             
17704         }, this);
17705         
17706         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17707         
17708         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17709
17710         if(this.modalTitle.length){
17711             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17712         }
17713
17714         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17715         
17716         if(this.mobile_restrict_height && listHeight < bodyHeight){
17717             this.touchViewBodyEl.setHeight(listHeight);
17718         }
17719         
17720         var _this = this;
17721         
17722         if(firstChecked && listHeight > bodyHeight){
17723             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17724         }
17725         
17726     },
17727     
17728     onTouchViewLoadException : function()
17729     {
17730         this.hideTouchView();
17731     },
17732     
17733     onTouchViewEmptyResults : function()
17734     {
17735         this.clearTouchView();
17736         
17737         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17738         
17739         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17740         
17741     },
17742     
17743     clearTouchView : function()
17744     {
17745         this.touchViewListGroup.dom.innerHTML = '';
17746     },
17747     
17748     onTouchViewClick : function(e, el, o)
17749     {
17750         e.preventDefault();
17751         
17752         var row = o.row;
17753         var rowIndex = o.rowIndex;
17754         
17755         var r = this.store.getAt(rowIndex);
17756         
17757         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17758             
17759             if(!this.multiple){
17760                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17761                     c.dom.removeAttribute('checked');
17762                 }, this);
17763
17764                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17765
17766                 this.setFromData(r.data);
17767
17768                 var close = this.closeTriggerEl();
17769
17770                 if(close){
17771                     close.show();
17772                 }
17773
17774                 this.hideTouchView();
17775
17776                 this.fireEvent('select', this, r, rowIndex);
17777
17778                 return;
17779             }
17780
17781             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17782                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17783                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17784                 return;
17785             }
17786
17787             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17788             this.addItem(r.data);
17789             this.tickItems.push(r.data);
17790         }
17791     },
17792     
17793     getAutoCreateNativeIOS : function()
17794     {
17795         var cfg = {
17796             cls: 'form-group' //input-group,
17797         };
17798         
17799         var combobox =  {
17800             tag: 'select',
17801             cls : 'roo-ios-select'
17802         };
17803         
17804         if (this.name) {
17805             combobox.name = this.name;
17806         }
17807         
17808         if (this.disabled) {
17809             combobox.disabled = true;
17810         }
17811         
17812         var settings = this;
17813         
17814         ['xs','sm','md','lg'].map(function(size){
17815             if (settings[size]) {
17816                 cfg.cls += ' col-' + size + '-' + settings[size];
17817             }
17818         });
17819         
17820         cfg.cn = combobox;
17821         
17822         return cfg;
17823         
17824     },
17825     
17826     initIOSView : function()
17827     {
17828         this.store.on('load', this.onIOSViewLoad, this);
17829         
17830         return;
17831     },
17832     
17833     onIOSViewLoad : function()
17834     {
17835         if(this.store.getCount() < 1){
17836             return;
17837         }
17838         
17839         this.clearIOSView();
17840         
17841         if(this.allowBlank) {
17842             
17843             var default_text = '-- SELECT --';
17844             
17845             if(this.placeholder.length){
17846                 default_text = this.placeholder;
17847             }
17848             
17849             if(this.emptyTitle.length){
17850                 default_text += ' - ' + this.emptyTitle + ' -';
17851             }
17852             
17853             var opt = this.inputEl().createChild({
17854                 tag: 'option',
17855                 value : 0,
17856                 html : default_text
17857             });
17858             
17859             var o = {};
17860             o[this.valueField] = 0;
17861             o[this.displayField] = default_text;
17862             
17863             this.ios_options.push({
17864                 data : o,
17865                 el : opt
17866             });
17867             
17868         }
17869         
17870         this.store.data.each(function(d, rowIndex){
17871             
17872             var html = '';
17873             
17874             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17875                 html = d.data[this.displayField];
17876             }
17877             
17878             var value = '';
17879             
17880             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17881                 value = d.data[this.valueField];
17882             }
17883             
17884             var option = {
17885                 tag: 'option',
17886                 value : value,
17887                 html : html
17888             };
17889             
17890             if(this.value == d.data[this.valueField]){
17891                 option['selected'] = true;
17892             }
17893             
17894             var opt = this.inputEl().createChild(option);
17895             
17896             this.ios_options.push({
17897                 data : d.data,
17898                 el : opt
17899             });
17900             
17901         }, this);
17902         
17903         this.inputEl().on('change', function(){
17904            this.fireEvent('select', this);
17905         }, this);
17906         
17907     },
17908     
17909     clearIOSView: function()
17910     {
17911         this.inputEl().dom.innerHTML = '';
17912         
17913         this.ios_options = [];
17914     },
17915     
17916     setIOSValue: function(v)
17917     {
17918         this.value = v;
17919         
17920         if(!this.ios_options){
17921             return;
17922         }
17923         
17924         Roo.each(this.ios_options, function(opts){
17925            
17926            opts.el.dom.removeAttribute('selected');
17927            
17928            if(opts.data[this.valueField] != v){
17929                return;
17930            }
17931            
17932            opts.el.dom.setAttribute('selected', true);
17933            
17934         }, this);
17935     }
17936
17937     /** 
17938     * @cfg {Boolean} grow 
17939     * @hide 
17940     */
17941     /** 
17942     * @cfg {Number} growMin 
17943     * @hide 
17944     */
17945     /** 
17946     * @cfg {Number} growMax 
17947     * @hide 
17948     */
17949     /**
17950      * @hide
17951      * @method autoSize
17952      */
17953 });
17954
17955 Roo.apply(Roo.bootstrap.ComboBox,  {
17956     
17957     header : {
17958         tag: 'div',
17959         cls: 'modal-header',
17960         cn: [
17961             {
17962                 tag: 'h4',
17963                 cls: 'modal-title'
17964             }
17965         ]
17966     },
17967     
17968     body : {
17969         tag: 'div',
17970         cls: 'modal-body',
17971         cn: [
17972             {
17973                 tag: 'ul',
17974                 cls: 'list-group'
17975             }
17976         ]
17977     },
17978     
17979     listItemRadio : {
17980         tag: 'li',
17981         cls: 'list-group-item',
17982         cn: [
17983             {
17984                 tag: 'span',
17985                 cls: 'roo-combobox-list-group-item-value'
17986             },
17987             {
17988                 tag: 'div',
17989                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17990                 cn: [
17991                     {
17992                         tag: 'input',
17993                         type: 'radio'
17994                     },
17995                     {
17996                         tag: 'label'
17997                     }
17998                 ]
17999             }
18000         ]
18001     },
18002     
18003     listItemCheckbox : {
18004         tag: 'li',
18005         cls: 'list-group-item',
18006         cn: [
18007             {
18008                 tag: 'span',
18009                 cls: 'roo-combobox-list-group-item-value'
18010             },
18011             {
18012                 tag: 'div',
18013                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18014                 cn: [
18015                     {
18016                         tag: 'input',
18017                         type: 'checkbox'
18018                     },
18019                     {
18020                         tag: 'label'
18021                     }
18022                 ]
18023             }
18024         ]
18025     },
18026     
18027     emptyResult : {
18028         tag: 'div',
18029         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18030     },
18031     
18032     footer : {
18033         tag: 'div',
18034         cls: 'modal-footer',
18035         cn: [
18036             {
18037                 tag: 'div',
18038                 cls: 'row',
18039                 cn: [
18040                     {
18041                         tag: 'div',
18042                         cls: 'col-xs-6 text-left',
18043                         cn: {
18044                             tag: 'button',
18045                             cls: 'btn btn-danger roo-touch-view-cancel',
18046                             html: 'Cancel'
18047                         }
18048                     },
18049                     {
18050                         tag: 'div',
18051                         cls: 'col-xs-6 text-right',
18052                         cn: {
18053                             tag: 'button',
18054                             cls: 'btn btn-success roo-touch-view-ok',
18055                             html: 'OK'
18056                         }
18057                     }
18058                 ]
18059             }
18060         ]
18061         
18062     }
18063 });
18064
18065 Roo.apply(Roo.bootstrap.ComboBox,  {
18066     
18067     touchViewTemplate : {
18068         tag: 'div',
18069         cls: 'modal fade roo-combobox-touch-view',
18070         cn: [
18071             {
18072                 tag: 'div',
18073                 cls: 'modal-dialog',
18074                 style : 'position:fixed', // we have to fix position....
18075                 cn: [
18076                     {
18077                         tag: 'div',
18078                         cls: 'modal-content',
18079                         cn: [
18080                             Roo.bootstrap.ComboBox.header,
18081                             Roo.bootstrap.ComboBox.body,
18082                             Roo.bootstrap.ComboBox.footer
18083                         ]
18084                     }
18085                 ]
18086             }
18087         ]
18088     }
18089 });/*
18090  * Based on:
18091  * Ext JS Library 1.1.1
18092  * Copyright(c) 2006-2007, Ext JS, LLC.
18093  *
18094  * Originally Released Under LGPL - original licence link has changed is not relivant.
18095  *
18096  * Fork - LGPL
18097  * <script type="text/javascript">
18098  */
18099
18100 /**
18101  * @class Roo.View
18102  * @extends Roo.util.Observable
18103  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18104  * This class also supports single and multi selection modes. <br>
18105  * Create a data model bound view:
18106  <pre><code>
18107  var store = new Roo.data.Store(...);
18108
18109  var view = new Roo.View({
18110     el : "my-element",
18111     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18112  
18113     singleSelect: true,
18114     selectedClass: "ydataview-selected",
18115     store: store
18116  });
18117
18118  // listen for node click?
18119  view.on("click", function(vw, index, node, e){
18120  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18121  });
18122
18123  // load XML data
18124  dataModel.load("foobar.xml");
18125  </code></pre>
18126  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18127  * <br><br>
18128  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18129  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18130  * 
18131  * Note: old style constructor is still suported (container, template, config)
18132  * 
18133  * @constructor
18134  * Create a new View
18135  * @param {Object} config The config object
18136  * 
18137  */
18138 Roo.View = function(config, depreciated_tpl, depreciated_config){
18139     
18140     this.parent = false;
18141     
18142     if (typeof(depreciated_tpl) == 'undefined') {
18143         // new way.. - universal constructor.
18144         Roo.apply(this, config);
18145         this.el  = Roo.get(this.el);
18146     } else {
18147         // old format..
18148         this.el  = Roo.get(config);
18149         this.tpl = depreciated_tpl;
18150         Roo.apply(this, depreciated_config);
18151     }
18152     this.wrapEl  = this.el.wrap().wrap();
18153     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18154     
18155     
18156     if(typeof(this.tpl) == "string"){
18157         this.tpl = new Roo.Template(this.tpl);
18158     } else {
18159         // support xtype ctors..
18160         this.tpl = new Roo.factory(this.tpl, Roo);
18161     }
18162     
18163     
18164     this.tpl.compile();
18165     
18166     /** @private */
18167     this.addEvents({
18168         /**
18169          * @event beforeclick
18170          * Fires before a click is processed. Returns false to cancel the default action.
18171          * @param {Roo.View} this
18172          * @param {Number} index The index of the target node
18173          * @param {HTMLElement} node The target node
18174          * @param {Roo.EventObject} e The raw event object
18175          */
18176             "beforeclick" : true,
18177         /**
18178          * @event click
18179          * Fires when a template node is clicked.
18180          * @param {Roo.View} this
18181          * @param {Number} index The index of the target node
18182          * @param {HTMLElement} node The target node
18183          * @param {Roo.EventObject} e The raw event object
18184          */
18185             "click" : true,
18186         /**
18187          * @event dblclick
18188          * Fires when a template node is double clicked.
18189          * @param {Roo.View} this
18190          * @param {Number} index The index of the target node
18191          * @param {HTMLElement} node The target node
18192          * @param {Roo.EventObject} e The raw event object
18193          */
18194             "dblclick" : true,
18195         /**
18196          * @event contextmenu
18197          * Fires when a template node is right clicked.
18198          * @param {Roo.View} this
18199          * @param {Number} index The index of the target node
18200          * @param {HTMLElement} node The target node
18201          * @param {Roo.EventObject} e The raw event object
18202          */
18203             "contextmenu" : true,
18204         /**
18205          * @event selectionchange
18206          * Fires when the selected nodes change.
18207          * @param {Roo.View} this
18208          * @param {Array} selections Array of the selected nodes
18209          */
18210             "selectionchange" : true,
18211     
18212         /**
18213          * @event beforeselect
18214          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18215          * @param {Roo.View} this
18216          * @param {HTMLElement} node The node to be selected
18217          * @param {Array} selections Array of currently selected nodes
18218          */
18219             "beforeselect" : true,
18220         /**
18221          * @event preparedata
18222          * Fires on every row to render, to allow you to change the data.
18223          * @param {Roo.View} this
18224          * @param {Object} data to be rendered (change this)
18225          */
18226           "preparedata" : true
18227           
18228           
18229         });
18230
18231
18232
18233     this.el.on({
18234         "click": this.onClick,
18235         "dblclick": this.onDblClick,
18236         "contextmenu": this.onContextMenu,
18237         scope:this
18238     });
18239
18240     this.selections = [];
18241     this.nodes = [];
18242     this.cmp = new Roo.CompositeElementLite([]);
18243     if(this.store){
18244         this.store = Roo.factory(this.store, Roo.data);
18245         this.setStore(this.store, true);
18246     }
18247     
18248     if ( this.footer && this.footer.xtype) {
18249            
18250          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18251         
18252         this.footer.dataSource = this.store;
18253         this.footer.container = fctr;
18254         this.footer = Roo.factory(this.footer, Roo);
18255         fctr.insertFirst(this.el);
18256         
18257         // this is a bit insane - as the paging toolbar seems to detach the el..
18258 //        dom.parentNode.parentNode.parentNode
18259          // they get detached?
18260     }
18261     
18262     
18263     Roo.View.superclass.constructor.call(this);
18264     
18265     
18266 };
18267
18268 Roo.extend(Roo.View, Roo.util.Observable, {
18269     
18270      /**
18271      * @cfg {Roo.data.Store} store Data store to load data from.
18272      */
18273     store : false,
18274     
18275     /**
18276      * @cfg {String|Roo.Element} el The container element.
18277      */
18278     el : '',
18279     
18280     /**
18281      * @cfg {String|Roo.Template} tpl The template used by this View 
18282      */
18283     tpl : false,
18284     /**
18285      * @cfg {String} dataName the named area of the template to use as the data area
18286      *                          Works with domtemplates roo-name="name"
18287      */
18288     dataName: false,
18289     /**
18290      * @cfg {String} selectedClass The css class to add to selected nodes
18291      */
18292     selectedClass : "x-view-selected",
18293      /**
18294      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18295      */
18296     emptyText : "",
18297     
18298     /**
18299      * @cfg {String} text to display on mask (default Loading)
18300      */
18301     mask : false,
18302     /**
18303      * @cfg {Boolean} multiSelect Allow multiple selection
18304      */
18305     multiSelect : false,
18306     /**
18307      * @cfg {Boolean} singleSelect Allow single selection
18308      */
18309     singleSelect:  false,
18310     
18311     /**
18312      * @cfg {Boolean} toggleSelect - selecting 
18313      */
18314     toggleSelect : false,
18315     
18316     /**
18317      * @cfg {Boolean} tickable - selecting 
18318      */
18319     tickable : false,
18320     
18321     /**
18322      * Returns the element this view is bound to.
18323      * @return {Roo.Element}
18324      */
18325     getEl : function(){
18326         return this.wrapEl;
18327     },
18328     
18329     
18330
18331     /**
18332      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18333      */
18334     refresh : function(){
18335         //Roo.log('refresh');
18336         var t = this.tpl;
18337         
18338         // if we are using something like 'domtemplate', then
18339         // the what gets used is:
18340         // t.applySubtemplate(NAME, data, wrapping data..)
18341         // the outer template then get' applied with
18342         //     the store 'extra data'
18343         // and the body get's added to the
18344         //      roo-name="data" node?
18345         //      <span class='roo-tpl-{name}'></span> ?????
18346         
18347         
18348         
18349         this.clearSelections();
18350         this.el.update("");
18351         var html = [];
18352         var records = this.store.getRange();
18353         if(records.length < 1) {
18354             
18355             // is this valid??  = should it render a template??
18356             
18357             this.el.update(this.emptyText);
18358             return;
18359         }
18360         var el = this.el;
18361         if (this.dataName) {
18362             this.el.update(t.apply(this.store.meta)); //????
18363             el = this.el.child('.roo-tpl-' + this.dataName);
18364         }
18365         
18366         for(var i = 0, len = records.length; i < len; i++){
18367             var data = this.prepareData(records[i].data, i, records[i]);
18368             this.fireEvent("preparedata", this, data, i, records[i]);
18369             
18370             var d = Roo.apply({}, data);
18371             
18372             if(this.tickable){
18373                 Roo.apply(d, {'roo-id' : Roo.id()});
18374                 
18375                 var _this = this;
18376             
18377                 Roo.each(this.parent.item, function(item){
18378                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18379                         return;
18380                     }
18381                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18382                 });
18383             }
18384             
18385             html[html.length] = Roo.util.Format.trim(
18386                 this.dataName ?
18387                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18388                     t.apply(d)
18389             );
18390         }
18391         
18392         
18393         
18394         el.update(html.join(""));
18395         this.nodes = el.dom.childNodes;
18396         this.updateIndexes(0);
18397     },
18398     
18399
18400     /**
18401      * Function to override to reformat the data that is sent to
18402      * the template for each node.
18403      * DEPRICATED - use the preparedata event handler.
18404      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18405      * a JSON object for an UpdateManager bound view).
18406      */
18407     prepareData : function(data, index, record)
18408     {
18409         this.fireEvent("preparedata", this, data, index, record);
18410         return data;
18411     },
18412
18413     onUpdate : function(ds, record){
18414         // Roo.log('on update');   
18415         this.clearSelections();
18416         var index = this.store.indexOf(record);
18417         var n = this.nodes[index];
18418         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18419         n.parentNode.removeChild(n);
18420         this.updateIndexes(index, index);
18421     },
18422
18423     
18424     
18425 // --------- FIXME     
18426     onAdd : function(ds, records, index)
18427     {
18428         //Roo.log(['on Add', ds, records, index] );        
18429         this.clearSelections();
18430         if(this.nodes.length == 0){
18431             this.refresh();
18432             return;
18433         }
18434         var n = this.nodes[index];
18435         for(var i = 0, len = records.length; i < len; i++){
18436             var d = this.prepareData(records[i].data, i, records[i]);
18437             if(n){
18438                 this.tpl.insertBefore(n, d);
18439             }else{
18440                 
18441                 this.tpl.append(this.el, d);
18442             }
18443         }
18444         this.updateIndexes(index);
18445     },
18446
18447     onRemove : function(ds, record, index){
18448        // Roo.log('onRemove');
18449         this.clearSelections();
18450         var el = this.dataName  ?
18451             this.el.child('.roo-tpl-' + this.dataName) :
18452             this.el; 
18453         
18454         el.dom.removeChild(this.nodes[index]);
18455         this.updateIndexes(index);
18456     },
18457
18458     /**
18459      * Refresh an individual node.
18460      * @param {Number} index
18461      */
18462     refreshNode : function(index){
18463         this.onUpdate(this.store, this.store.getAt(index));
18464     },
18465
18466     updateIndexes : function(startIndex, endIndex){
18467         var ns = this.nodes;
18468         startIndex = startIndex || 0;
18469         endIndex = endIndex || ns.length - 1;
18470         for(var i = startIndex; i <= endIndex; i++){
18471             ns[i].nodeIndex = i;
18472         }
18473     },
18474
18475     /**
18476      * Changes the data store this view uses and refresh the view.
18477      * @param {Store} store
18478      */
18479     setStore : function(store, initial){
18480         if(!initial && this.store){
18481             this.store.un("datachanged", this.refresh);
18482             this.store.un("add", this.onAdd);
18483             this.store.un("remove", this.onRemove);
18484             this.store.un("update", this.onUpdate);
18485             this.store.un("clear", this.refresh);
18486             this.store.un("beforeload", this.onBeforeLoad);
18487             this.store.un("load", this.onLoad);
18488             this.store.un("loadexception", this.onLoad);
18489         }
18490         if(store){
18491           
18492             store.on("datachanged", this.refresh, this);
18493             store.on("add", this.onAdd, this);
18494             store.on("remove", this.onRemove, this);
18495             store.on("update", this.onUpdate, this);
18496             store.on("clear", this.refresh, this);
18497             store.on("beforeload", this.onBeforeLoad, this);
18498             store.on("load", this.onLoad, this);
18499             store.on("loadexception", this.onLoad, this);
18500         }
18501         
18502         if(store){
18503             this.refresh();
18504         }
18505     },
18506     /**
18507      * onbeforeLoad - masks the loading area.
18508      *
18509      */
18510     onBeforeLoad : function(store,opts)
18511     {
18512          //Roo.log('onBeforeLoad');   
18513         if (!opts.add) {
18514             this.el.update("");
18515         }
18516         this.el.mask(this.mask ? this.mask : "Loading" ); 
18517     },
18518     onLoad : function ()
18519     {
18520         this.el.unmask();
18521     },
18522     
18523
18524     /**
18525      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18526      * @param {HTMLElement} node
18527      * @return {HTMLElement} The template node
18528      */
18529     findItemFromChild : function(node){
18530         var el = this.dataName  ?
18531             this.el.child('.roo-tpl-' + this.dataName,true) :
18532             this.el.dom; 
18533         
18534         if(!node || node.parentNode == el){
18535                     return node;
18536             }
18537             var p = node.parentNode;
18538             while(p && p != el){
18539             if(p.parentNode == el){
18540                 return p;
18541             }
18542             p = p.parentNode;
18543         }
18544             return null;
18545     },
18546
18547     /** @ignore */
18548     onClick : function(e){
18549         var item = this.findItemFromChild(e.getTarget());
18550         if(item){
18551             var index = this.indexOf(item);
18552             if(this.onItemClick(item, index, e) !== false){
18553                 this.fireEvent("click", this, index, item, e);
18554             }
18555         }else{
18556             this.clearSelections();
18557         }
18558     },
18559
18560     /** @ignore */
18561     onContextMenu : function(e){
18562         var item = this.findItemFromChild(e.getTarget());
18563         if(item){
18564             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18565         }
18566     },
18567
18568     /** @ignore */
18569     onDblClick : function(e){
18570         var item = this.findItemFromChild(e.getTarget());
18571         if(item){
18572             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18573         }
18574     },
18575
18576     onItemClick : function(item, index, e)
18577     {
18578         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18579             return false;
18580         }
18581         if (this.toggleSelect) {
18582             var m = this.isSelected(item) ? 'unselect' : 'select';
18583             //Roo.log(m);
18584             var _t = this;
18585             _t[m](item, true, false);
18586             return true;
18587         }
18588         if(this.multiSelect || this.singleSelect){
18589             if(this.multiSelect && e.shiftKey && this.lastSelection){
18590                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18591             }else{
18592                 this.select(item, this.multiSelect && e.ctrlKey);
18593                 this.lastSelection = item;
18594             }
18595             
18596             if(!this.tickable){
18597                 e.preventDefault();
18598             }
18599             
18600         }
18601         return true;
18602     },
18603
18604     /**
18605      * Get the number of selected nodes.
18606      * @return {Number}
18607      */
18608     getSelectionCount : function(){
18609         return this.selections.length;
18610     },
18611
18612     /**
18613      * Get the currently selected nodes.
18614      * @return {Array} An array of HTMLElements
18615      */
18616     getSelectedNodes : function(){
18617         return this.selections;
18618     },
18619
18620     /**
18621      * Get the indexes of the selected nodes.
18622      * @return {Array}
18623      */
18624     getSelectedIndexes : function(){
18625         var indexes = [], s = this.selections;
18626         for(var i = 0, len = s.length; i < len; i++){
18627             indexes.push(s[i].nodeIndex);
18628         }
18629         return indexes;
18630     },
18631
18632     /**
18633      * Clear all selections
18634      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18635      */
18636     clearSelections : function(suppressEvent){
18637         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18638             this.cmp.elements = this.selections;
18639             this.cmp.removeClass(this.selectedClass);
18640             this.selections = [];
18641             if(!suppressEvent){
18642                 this.fireEvent("selectionchange", this, this.selections);
18643             }
18644         }
18645     },
18646
18647     /**
18648      * Returns true if the passed node is selected
18649      * @param {HTMLElement/Number} node The node or node index
18650      * @return {Boolean}
18651      */
18652     isSelected : function(node){
18653         var s = this.selections;
18654         if(s.length < 1){
18655             return false;
18656         }
18657         node = this.getNode(node);
18658         return s.indexOf(node) !== -1;
18659     },
18660
18661     /**
18662      * Selects nodes.
18663      * @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
18664      * @param {Boolean} keepExisting (optional) true to keep existing selections
18665      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18666      */
18667     select : function(nodeInfo, keepExisting, suppressEvent){
18668         if(nodeInfo instanceof Array){
18669             if(!keepExisting){
18670                 this.clearSelections(true);
18671             }
18672             for(var i = 0, len = nodeInfo.length; i < len; i++){
18673                 this.select(nodeInfo[i], true, true);
18674             }
18675             return;
18676         } 
18677         var node = this.getNode(nodeInfo);
18678         if(!node || this.isSelected(node)){
18679             return; // already selected.
18680         }
18681         if(!keepExisting){
18682             this.clearSelections(true);
18683         }
18684         
18685         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18686             Roo.fly(node).addClass(this.selectedClass);
18687             this.selections.push(node);
18688             if(!suppressEvent){
18689                 this.fireEvent("selectionchange", this, this.selections);
18690             }
18691         }
18692         
18693         
18694     },
18695       /**
18696      * Unselects nodes.
18697      * @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
18698      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18699      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18700      */
18701     unselect : function(nodeInfo, keepExisting, suppressEvent)
18702     {
18703         if(nodeInfo instanceof Array){
18704             Roo.each(this.selections, function(s) {
18705                 this.unselect(s, nodeInfo);
18706             }, this);
18707             return;
18708         }
18709         var node = this.getNode(nodeInfo);
18710         if(!node || !this.isSelected(node)){
18711             //Roo.log("not selected");
18712             return; // not selected.
18713         }
18714         // fireevent???
18715         var ns = [];
18716         Roo.each(this.selections, function(s) {
18717             if (s == node ) {
18718                 Roo.fly(node).removeClass(this.selectedClass);
18719
18720                 return;
18721             }
18722             ns.push(s);
18723         },this);
18724         
18725         this.selections= ns;
18726         this.fireEvent("selectionchange", this, this.selections);
18727     },
18728
18729     /**
18730      * Gets a template node.
18731      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18732      * @return {HTMLElement} The node or null if it wasn't found
18733      */
18734     getNode : function(nodeInfo){
18735         if(typeof nodeInfo == "string"){
18736             return document.getElementById(nodeInfo);
18737         }else if(typeof nodeInfo == "number"){
18738             return this.nodes[nodeInfo];
18739         }
18740         return nodeInfo;
18741     },
18742
18743     /**
18744      * Gets a range template nodes.
18745      * @param {Number} startIndex
18746      * @param {Number} endIndex
18747      * @return {Array} An array of nodes
18748      */
18749     getNodes : function(start, end){
18750         var ns = this.nodes;
18751         start = start || 0;
18752         end = typeof end == "undefined" ? ns.length - 1 : end;
18753         var nodes = [];
18754         if(start <= end){
18755             for(var i = start; i <= end; i++){
18756                 nodes.push(ns[i]);
18757             }
18758         } else{
18759             for(var i = start; i >= end; i--){
18760                 nodes.push(ns[i]);
18761             }
18762         }
18763         return nodes;
18764     },
18765
18766     /**
18767      * Finds the index of the passed node
18768      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18769      * @return {Number} The index of the node or -1
18770      */
18771     indexOf : function(node){
18772         node = this.getNode(node);
18773         if(typeof node.nodeIndex == "number"){
18774             return node.nodeIndex;
18775         }
18776         var ns = this.nodes;
18777         for(var i = 0, len = ns.length; i < len; i++){
18778             if(ns[i] == node){
18779                 return i;
18780             }
18781         }
18782         return -1;
18783     }
18784 });
18785 /*
18786  * - LGPL
18787  *
18788  * based on jquery fullcalendar
18789  * 
18790  */
18791
18792 Roo.bootstrap = Roo.bootstrap || {};
18793 /**
18794  * @class Roo.bootstrap.Calendar
18795  * @extends Roo.bootstrap.Component
18796  * Bootstrap Calendar class
18797  * @cfg {Boolean} loadMask (true|false) default false
18798  * @cfg {Object} header generate the user specific header of the calendar, default false
18799
18800  * @constructor
18801  * Create a new Container
18802  * @param {Object} config The config object
18803  */
18804
18805
18806
18807 Roo.bootstrap.Calendar = function(config){
18808     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18809      this.addEvents({
18810         /**
18811              * @event select
18812              * Fires when a date is selected
18813              * @param {DatePicker} this
18814              * @param {Date} date The selected date
18815              */
18816         'select': true,
18817         /**
18818              * @event monthchange
18819              * Fires when the displayed month changes 
18820              * @param {DatePicker} this
18821              * @param {Date} date The selected month
18822              */
18823         'monthchange': true,
18824         /**
18825              * @event evententer
18826              * Fires when mouse over an event
18827              * @param {Calendar} this
18828              * @param {event} Event
18829              */
18830         'evententer': true,
18831         /**
18832              * @event eventleave
18833              * Fires when the mouse leaves an
18834              * @param {Calendar} this
18835              * @param {event}
18836              */
18837         'eventleave': true,
18838         /**
18839              * @event eventclick
18840              * Fires when the mouse click an
18841              * @param {Calendar} this
18842              * @param {event}
18843              */
18844         'eventclick': true
18845         
18846     });
18847
18848 };
18849
18850 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18851     
18852      /**
18853      * @cfg {Number} startDay
18854      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18855      */
18856     startDay : 0,
18857     
18858     loadMask : false,
18859     
18860     header : false,
18861       
18862     getAutoCreate : function(){
18863         
18864         
18865         var fc_button = function(name, corner, style, content ) {
18866             return Roo.apply({},{
18867                 tag : 'span',
18868                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18869                          (corner.length ?
18870                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18871                             ''
18872                         ),
18873                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18874                 unselectable: 'on'
18875             });
18876         };
18877         
18878         var header = {};
18879         
18880         if(!this.header){
18881             header = {
18882                 tag : 'table',
18883                 cls : 'fc-header',
18884                 style : 'width:100%',
18885                 cn : [
18886                     {
18887                         tag: 'tr',
18888                         cn : [
18889                             {
18890                                 tag : 'td',
18891                                 cls : 'fc-header-left',
18892                                 cn : [
18893                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18894                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18895                                     { tag: 'span', cls: 'fc-header-space' },
18896                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18897
18898
18899                                 ]
18900                             },
18901
18902                             {
18903                                 tag : 'td',
18904                                 cls : 'fc-header-center',
18905                                 cn : [
18906                                     {
18907                                         tag: 'span',
18908                                         cls: 'fc-header-title',
18909                                         cn : {
18910                                             tag: 'H2',
18911                                             html : 'month / year'
18912                                         }
18913                                     }
18914
18915                                 ]
18916                             },
18917                             {
18918                                 tag : 'td',
18919                                 cls : 'fc-header-right',
18920                                 cn : [
18921                               /*      fc_button('month', 'left', '', 'month' ),
18922                                     fc_button('week', '', '', 'week' ),
18923                                     fc_button('day', 'right', '', 'day' )
18924                                 */    
18925
18926                                 ]
18927                             }
18928
18929                         ]
18930                     }
18931                 ]
18932             };
18933         }
18934         
18935         header = this.header;
18936         
18937        
18938         var cal_heads = function() {
18939             var ret = [];
18940             // fixme - handle this.
18941             
18942             for (var i =0; i < Date.dayNames.length; i++) {
18943                 var d = Date.dayNames[i];
18944                 ret.push({
18945                     tag: 'th',
18946                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18947                     html : d.substring(0,3)
18948                 });
18949                 
18950             }
18951             ret[0].cls += ' fc-first';
18952             ret[6].cls += ' fc-last';
18953             return ret;
18954         };
18955         var cal_cell = function(n) {
18956             return  {
18957                 tag: 'td',
18958                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18959                 cn : [
18960                     {
18961                         cn : [
18962                             {
18963                                 cls: 'fc-day-number',
18964                                 html: 'D'
18965                             },
18966                             {
18967                                 cls: 'fc-day-content',
18968                              
18969                                 cn : [
18970                                      {
18971                                         style: 'position: relative;' // height: 17px;
18972                                     }
18973                                 ]
18974                             }
18975                             
18976                             
18977                         ]
18978                     }
18979                 ]
18980                 
18981             }
18982         };
18983         var cal_rows = function() {
18984             
18985             var ret = [];
18986             for (var r = 0; r < 6; r++) {
18987                 var row= {
18988                     tag : 'tr',
18989                     cls : 'fc-week',
18990                     cn : []
18991                 };
18992                 
18993                 for (var i =0; i < Date.dayNames.length; i++) {
18994                     var d = Date.dayNames[i];
18995                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18996
18997                 }
18998                 row.cn[0].cls+=' fc-first';
18999                 row.cn[0].cn[0].style = 'min-height:90px';
19000                 row.cn[6].cls+=' fc-last';
19001                 ret.push(row);
19002                 
19003             }
19004             ret[0].cls += ' fc-first';
19005             ret[4].cls += ' fc-prev-last';
19006             ret[5].cls += ' fc-last';
19007             return ret;
19008             
19009         };
19010         
19011         var cal_table = {
19012             tag: 'table',
19013             cls: 'fc-border-separate',
19014             style : 'width:100%',
19015             cellspacing  : 0,
19016             cn : [
19017                 { 
19018                     tag: 'thead',
19019                     cn : [
19020                         { 
19021                             tag: 'tr',
19022                             cls : 'fc-first fc-last',
19023                             cn : cal_heads()
19024                         }
19025                     ]
19026                 },
19027                 { 
19028                     tag: 'tbody',
19029                     cn : cal_rows()
19030                 }
19031                   
19032             ]
19033         };
19034          
19035          var cfg = {
19036             cls : 'fc fc-ltr',
19037             cn : [
19038                 header,
19039                 {
19040                     cls : 'fc-content',
19041                     style : "position: relative;",
19042                     cn : [
19043                         {
19044                             cls : 'fc-view fc-view-month fc-grid',
19045                             style : 'position: relative',
19046                             unselectable : 'on',
19047                             cn : [
19048                                 {
19049                                     cls : 'fc-event-container',
19050                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19051                                 },
19052                                 cal_table
19053                             ]
19054                         }
19055                     ]
19056     
19057                 }
19058            ] 
19059             
19060         };
19061         
19062          
19063         
19064         return cfg;
19065     },
19066     
19067     
19068     initEvents : function()
19069     {
19070         if(!this.store){
19071             throw "can not find store for calendar";
19072         }
19073         
19074         var mark = {
19075             tag: "div",
19076             cls:"x-dlg-mask",
19077             style: "text-align:center",
19078             cn: [
19079                 {
19080                     tag: "div",
19081                     style: "background-color:white;width:50%;margin:250 auto",
19082                     cn: [
19083                         {
19084                             tag: "img",
19085                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19086                         },
19087                         {
19088                             tag: "span",
19089                             html: "Loading"
19090                         }
19091                         
19092                     ]
19093                 }
19094             ]
19095         };
19096         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19097         
19098         var size = this.el.select('.fc-content', true).first().getSize();
19099         this.maskEl.setSize(size.width, size.height);
19100         this.maskEl.enableDisplayMode("block");
19101         if(!this.loadMask){
19102             this.maskEl.hide();
19103         }
19104         
19105         this.store = Roo.factory(this.store, Roo.data);
19106         this.store.on('load', this.onLoad, this);
19107         this.store.on('beforeload', this.onBeforeLoad, this);
19108         
19109         this.resize();
19110         
19111         this.cells = this.el.select('.fc-day',true);
19112         //Roo.log(this.cells);
19113         this.textNodes = this.el.query('.fc-day-number');
19114         this.cells.addClassOnOver('fc-state-hover');
19115         
19116         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19117         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19118         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19119         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19120         
19121         this.on('monthchange', this.onMonthChange, this);
19122         
19123         this.update(new Date().clearTime());
19124     },
19125     
19126     resize : function() {
19127         var sz  = this.el.getSize();
19128         
19129         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19130         this.el.select('.fc-day-content div',true).setHeight(34);
19131     },
19132     
19133     
19134     // private
19135     showPrevMonth : function(e){
19136         this.update(this.activeDate.add("mo", -1));
19137     },
19138     showToday : function(e){
19139         this.update(new Date().clearTime());
19140     },
19141     // private
19142     showNextMonth : function(e){
19143         this.update(this.activeDate.add("mo", 1));
19144     },
19145
19146     // private
19147     showPrevYear : function(){
19148         this.update(this.activeDate.add("y", -1));
19149     },
19150
19151     // private
19152     showNextYear : function(){
19153         this.update(this.activeDate.add("y", 1));
19154     },
19155
19156     
19157    // private
19158     update : function(date)
19159     {
19160         var vd = this.activeDate;
19161         this.activeDate = date;
19162 //        if(vd && this.el){
19163 //            var t = date.getTime();
19164 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19165 //                Roo.log('using add remove');
19166 //                
19167 //                this.fireEvent('monthchange', this, date);
19168 //                
19169 //                this.cells.removeClass("fc-state-highlight");
19170 //                this.cells.each(function(c){
19171 //                   if(c.dateValue == t){
19172 //                       c.addClass("fc-state-highlight");
19173 //                       setTimeout(function(){
19174 //                            try{c.dom.firstChild.focus();}catch(e){}
19175 //                       }, 50);
19176 //                       return false;
19177 //                   }
19178 //                   return true;
19179 //                });
19180 //                return;
19181 //            }
19182 //        }
19183         
19184         var days = date.getDaysInMonth();
19185         
19186         var firstOfMonth = date.getFirstDateOfMonth();
19187         var startingPos = firstOfMonth.getDay()-this.startDay;
19188         
19189         if(startingPos < this.startDay){
19190             startingPos += 7;
19191         }
19192         
19193         var pm = date.add(Date.MONTH, -1);
19194         var prevStart = pm.getDaysInMonth()-startingPos;
19195 //        
19196         this.cells = this.el.select('.fc-day',true);
19197         this.textNodes = this.el.query('.fc-day-number');
19198         this.cells.addClassOnOver('fc-state-hover');
19199         
19200         var cells = this.cells.elements;
19201         var textEls = this.textNodes;
19202         
19203         Roo.each(cells, function(cell){
19204             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19205         });
19206         
19207         days += startingPos;
19208
19209         // convert everything to numbers so it's fast
19210         var day = 86400000;
19211         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19212         //Roo.log(d);
19213         //Roo.log(pm);
19214         //Roo.log(prevStart);
19215         
19216         var today = new Date().clearTime().getTime();
19217         var sel = date.clearTime().getTime();
19218         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19219         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19220         var ddMatch = this.disabledDatesRE;
19221         var ddText = this.disabledDatesText;
19222         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19223         var ddaysText = this.disabledDaysText;
19224         var format = this.format;
19225         
19226         var setCellClass = function(cal, cell){
19227             cell.row = 0;
19228             cell.events = [];
19229             cell.more = [];
19230             //Roo.log('set Cell Class');
19231             cell.title = "";
19232             var t = d.getTime();
19233             
19234             //Roo.log(d);
19235             
19236             cell.dateValue = t;
19237             if(t == today){
19238                 cell.className += " fc-today";
19239                 cell.className += " fc-state-highlight";
19240                 cell.title = cal.todayText;
19241             }
19242             if(t == sel){
19243                 // disable highlight in other month..
19244                 //cell.className += " fc-state-highlight";
19245                 
19246             }
19247             // disabling
19248             if(t < min) {
19249                 cell.className = " fc-state-disabled";
19250                 cell.title = cal.minText;
19251                 return;
19252             }
19253             if(t > max) {
19254                 cell.className = " fc-state-disabled";
19255                 cell.title = cal.maxText;
19256                 return;
19257             }
19258             if(ddays){
19259                 if(ddays.indexOf(d.getDay()) != -1){
19260                     cell.title = ddaysText;
19261                     cell.className = " fc-state-disabled";
19262                 }
19263             }
19264             if(ddMatch && format){
19265                 var fvalue = d.dateFormat(format);
19266                 if(ddMatch.test(fvalue)){
19267                     cell.title = ddText.replace("%0", fvalue);
19268                     cell.className = " fc-state-disabled";
19269                 }
19270             }
19271             
19272             if (!cell.initialClassName) {
19273                 cell.initialClassName = cell.dom.className;
19274             }
19275             
19276             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19277         };
19278
19279         var i = 0;
19280         
19281         for(; i < startingPos; i++) {
19282             textEls[i].innerHTML = (++prevStart);
19283             d.setDate(d.getDate()+1);
19284             
19285             cells[i].className = "fc-past fc-other-month";
19286             setCellClass(this, cells[i]);
19287         }
19288         
19289         var intDay = 0;
19290         
19291         for(; i < days; i++){
19292             intDay = i - startingPos + 1;
19293             textEls[i].innerHTML = (intDay);
19294             d.setDate(d.getDate()+1);
19295             
19296             cells[i].className = ''; // "x-date-active";
19297             setCellClass(this, cells[i]);
19298         }
19299         var extraDays = 0;
19300         
19301         for(; i < 42; i++) {
19302             textEls[i].innerHTML = (++extraDays);
19303             d.setDate(d.getDate()+1);
19304             
19305             cells[i].className = "fc-future fc-other-month";
19306             setCellClass(this, cells[i]);
19307         }
19308         
19309         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19310         
19311         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19312         
19313         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19314         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19315         
19316         if(totalRows != 6){
19317             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19318             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19319         }
19320         
19321         this.fireEvent('monthchange', this, date);
19322         
19323         
19324         /*
19325         if(!this.internalRender){
19326             var main = this.el.dom.firstChild;
19327             var w = main.offsetWidth;
19328             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19329             Roo.fly(main).setWidth(w);
19330             this.internalRender = true;
19331             // opera does not respect the auto grow header center column
19332             // then, after it gets a width opera refuses to recalculate
19333             // without a second pass
19334             if(Roo.isOpera && !this.secondPass){
19335                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19336                 this.secondPass = true;
19337                 this.update.defer(10, this, [date]);
19338             }
19339         }
19340         */
19341         
19342     },
19343     
19344     findCell : function(dt) {
19345         dt = dt.clearTime().getTime();
19346         var ret = false;
19347         this.cells.each(function(c){
19348             //Roo.log("check " +c.dateValue + '?=' + dt);
19349             if(c.dateValue == dt){
19350                 ret = c;
19351                 return false;
19352             }
19353             return true;
19354         });
19355         
19356         return ret;
19357     },
19358     
19359     findCells : function(ev) {
19360         var s = ev.start.clone().clearTime().getTime();
19361        // Roo.log(s);
19362         var e= ev.end.clone().clearTime().getTime();
19363        // Roo.log(e);
19364         var ret = [];
19365         this.cells.each(function(c){
19366              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19367             
19368             if(c.dateValue > e){
19369                 return ;
19370             }
19371             if(c.dateValue < s){
19372                 return ;
19373             }
19374             ret.push(c);
19375         });
19376         
19377         return ret;    
19378     },
19379     
19380 //    findBestRow: function(cells)
19381 //    {
19382 //        var ret = 0;
19383 //        
19384 //        for (var i =0 ; i < cells.length;i++) {
19385 //            ret  = Math.max(cells[i].rows || 0,ret);
19386 //        }
19387 //        return ret;
19388 //        
19389 //    },
19390     
19391     
19392     addItem : function(ev)
19393     {
19394         // look for vertical location slot in
19395         var cells = this.findCells(ev);
19396         
19397 //        ev.row = this.findBestRow(cells);
19398         
19399         // work out the location.
19400         
19401         var crow = false;
19402         var rows = [];
19403         for(var i =0; i < cells.length; i++) {
19404             
19405             cells[i].row = cells[0].row;
19406             
19407             if(i == 0){
19408                 cells[i].row = cells[i].row + 1;
19409             }
19410             
19411             if (!crow) {
19412                 crow = {
19413                     start : cells[i],
19414                     end :  cells[i]
19415                 };
19416                 continue;
19417             }
19418             if (crow.start.getY() == cells[i].getY()) {
19419                 // on same row.
19420                 crow.end = cells[i];
19421                 continue;
19422             }
19423             // different row.
19424             rows.push(crow);
19425             crow = {
19426                 start: cells[i],
19427                 end : cells[i]
19428             };
19429             
19430         }
19431         
19432         rows.push(crow);
19433         ev.els = [];
19434         ev.rows = rows;
19435         ev.cells = cells;
19436         
19437         cells[0].events.push(ev);
19438         
19439         this.calevents.push(ev);
19440     },
19441     
19442     clearEvents: function() {
19443         
19444         if(!this.calevents){
19445             return;
19446         }
19447         
19448         Roo.each(this.cells.elements, function(c){
19449             c.row = 0;
19450             c.events = [];
19451             c.more = [];
19452         });
19453         
19454         Roo.each(this.calevents, function(e) {
19455             Roo.each(e.els, function(el) {
19456                 el.un('mouseenter' ,this.onEventEnter, this);
19457                 el.un('mouseleave' ,this.onEventLeave, this);
19458                 el.remove();
19459             },this);
19460         },this);
19461         
19462         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19463             e.remove();
19464         });
19465         
19466     },
19467     
19468     renderEvents: function()
19469     {   
19470         var _this = this;
19471         
19472         this.cells.each(function(c) {
19473             
19474             if(c.row < 5){
19475                 return;
19476             }
19477             
19478             var ev = c.events;
19479             
19480             var r = 4;
19481             if(c.row != c.events.length){
19482                 r = 4 - (4 - (c.row - c.events.length));
19483             }
19484             
19485             c.events = ev.slice(0, r);
19486             c.more = ev.slice(r);
19487             
19488             if(c.more.length && c.more.length == 1){
19489                 c.events.push(c.more.pop());
19490             }
19491             
19492             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19493             
19494         });
19495             
19496         this.cells.each(function(c) {
19497             
19498             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19499             
19500             
19501             for (var e = 0; e < c.events.length; e++){
19502                 var ev = c.events[e];
19503                 var rows = ev.rows;
19504                 
19505                 for(var i = 0; i < rows.length; i++) {
19506                 
19507                     // how many rows should it span..
19508
19509                     var  cfg = {
19510                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19511                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19512
19513                         unselectable : "on",
19514                         cn : [
19515                             {
19516                                 cls: 'fc-event-inner',
19517                                 cn : [
19518     //                                {
19519     //                                  tag:'span',
19520     //                                  cls: 'fc-event-time',
19521     //                                  html : cells.length > 1 ? '' : ev.time
19522     //                                },
19523                                     {
19524                                       tag:'span',
19525                                       cls: 'fc-event-title',
19526                                       html : String.format('{0}', ev.title)
19527                                     }
19528
19529
19530                                 ]
19531                             },
19532                             {
19533                                 cls: 'ui-resizable-handle ui-resizable-e',
19534                                 html : '&nbsp;&nbsp;&nbsp'
19535                             }
19536
19537                         ]
19538                     };
19539
19540                     if (i == 0) {
19541                         cfg.cls += ' fc-event-start';
19542                     }
19543                     if ((i+1) == rows.length) {
19544                         cfg.cls += ' fc-event-end';
19545                     }
19546
19547                     var ctr = _this.el.select('.fc-event-container',true).first();
19548                     var cg = ctr.createChild(cfg);
19549
19550                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19551                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19552
19553                     var r = (c.more.length) ? 1 : 0;
19554                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19555                     cg.setWidth(ebox.right - sbox.x -2);
19556
19557                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19558                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19559                     cg.on('click', _this.onEventClick, _this, ev);
19560
19561                     ev.els.push(cg);
19562                     
19563                 }
19564                 
19565             }
19566             
19567             
19568             if(c.more.length){
19569                 var  cfg = {
19570                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19571                     style : 'position: absolute',
19572                     unselectable : "on",
19573                     cn : [
19574                         {
19575                             cls: 'fc-event-inner',
19576                             cn : [
19577                                 {
19578                                   tag:'span',
19579                                   cls: 'fc-event-title',
19580                                   html : 'More'
19581                                 }
19582
19583
19584                             ]
19585                         },
19586                         {
19587                             cls: 'ui-resizable-handle ui-resizable-e',
19588                             html : '&nbsp;&nbsp;&nbsp'
19589                         }
19590
19591                     ]
19592                 };
19593
19594                 var ctr = _this.el.select('.fc-event-container',true).first();
19595                 var cg = ctr.createChild(cfg);
19596
19597                 var sbox = c.select('.fc-day-content',true).first().getBox();
19598                 var ebox = c.select('.fc-day-content',true).first().getBox();
19599                 //Roo.log(cg);
19600                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19601                 cg.setWidth(ebox.right - sbox.x -2);
19602
19603                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19604                 
19605             }
19606             
19607         });
19608         
19609         
19610         
19611     },
19612     
19613     onEventEnter: function (e, el,event,d) {
19614         this.fireEvent('evententer', this, el, event);
19615     },
19616     
19617     onEventLeave: function (e, el,event,d) {
19618         this.fireEvent('eventleave', this, el, event);
19619     },
19620     
19621     onEventClick: function (e, el,event,d) {
19622         this.fireEvent('eventclick', this, el, event);
19623     },
19624     
19625     onMonthChange: function () {
19626         this.store.load();
19627     },
19628     
19629     onMoreEventClick: function(e, el, more)
19630     {
19631         var _this = this;
19632         
19633         this.calpopover.placement = 'right';
19634         this.calpopover.setTitle('More');
19635         
19636         this.calpopover.setContent('');
19637         
19638         var ctr = this.calpopover.el.select('.popover-content', true).first();
19639         
19640         Roo.each(more, function(m){
19641             var cfg = {
19642                 cls : 'fc-event-hori fc-event-draggable',
19643                 html : m.title
19644             };
19645             var cg = ctr.createChild(cfg);
19646             
19647             cg.on('click', _this.onEventClick, _this, m);
19648         });
19649         
19650         this.calpopover.show(el);
19651         
19652         
19653     },
19654     
19655     onLoad: function () 
19656     {   
19657         this.calevents = [];
19658         var cal = this;
19659         
19660         if(this.store.getCount() > 0){
19661             this.store.data.each(function(d){
19662                cal.addItem({
19663                     id : d.data.id,
19664                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19665                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19666                     time : d.data.start_time,
19667                     title : d.data.title,
19668                     description : d.data.description,
19669                     venue : d.data.venue
19670                 });
19671             });
19672         }
19673         
19674         this.renderEvents();
19675         
19676         if(this.calevents.length && this.loadMask){
19677             this.maskEl.hide();
19678         }
19679     },
19680     
19681     onBeforeLoad: function()
19682     {
19683         this.clearEvents();
19684         if(this.loadMask){
19685             this.maskEl.show();
19686         }
19687     }
19688 });
19689
19690  
19691  /*
19692  * - LGPL
19693  *
19694  * element
19695  * 
19696  */
19697
19698 /**
19699  * @class Roo.bootstrap.Popover
19700  * @extends Roo.bootstrap.Component
19701  * Bootstrap Popover class
19702  * @cfg {String} html contents of the popover   (or false to use children..)
19703  * @cfg {String} title of popover (or false to hide)
19704  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19705  * @cfg {String} trigger click || hover (or false to trigger manually)
19706  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19707  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19708  *      - if false and it has a 'parent' then it will be automatically added to that element
19709  *      - if string - Roo.get  will be called 
19710  * @cfg {Number} delay - delay before showing
19711  
19712  * @constructor
19713  * Create a new Popover
19714  * @param {Object} config The config object
19715  */
19716
19717 Roo.bootstrap.Popover = function(config){
19718     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19719     
19720     this.addEvents({
19721         // raw events
19722          /**
19723          * @event show
19724          * After the popover show
19725          * 
19726          * @param {Roo.bootstrap.Popover} this
19727          */
19728         "show" : true,
19729         /**
19730          * @event hide
19731          * After the popover hide
19732          * 
19733          * @param {Roo.bootstrap.Popover} this
19734          */
19735         "hide" : true
19736     });
19737 };
19738
19739 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19740     
19741     title: false,
19742     html: false,
19743     
19744     placement : 'right',
19745     trigger : 'hover', // hover
19746     modal : false,
19747     delay : 0,
19748     
19749     over: false,
19750     
19751     can_build_overlaid : false,
19752     
19753     maskEl : false, // the mask element
19754     headerEl : false,
19755     contentEl : false,
19756     alignEl : false, // when show is called with an element - this get's stored.
19757     
19758     getChildContainer : function()
19759     {
19760         return this.contentEl;
19761         
19762     },
19763     getPopoverHeader : function()
19764     {
19765         this.title = true; // flag not to hide it..
19766         this.headerEl.addClass('p-0');
19767         return this.headerEl
19768     },
19769     
19770     
19771     getAutoCreate : function(){
19772          
19773         var cfg = {
19774            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19775            style: 'display:block',
19776            cn : [
19777                 {
19778                     cls : 'arrow'
19779                 },
19780                 {
19781                     cls : 'popover-inner ',
19782                     cn : [
19783                         {
19784                             tag: 'h3',
19785                             cls: 'popover-title popover-header',
19786                             html : this.title === false ? '' : this.title
19787                         },
19788                         {
19789                             cls : 'popover-content popover-body '  + (this.cls || ''),
19790                             html : this.html || ''
19791                         }
19792                     ]
19793                     
19794                 }
19795            ]
19796         };
19797         
19798         return cfg;
19799     },
19800     /**
19801      * @param {string} the title
19802      */
19803     setTitle: function(str)
19804     {
19805         this.title = str;
19806         if (this.el) {
19807             this.headerEl.dom.innerHTML = str;
19808         }
19809         
19810     },
19811     /**
19812      * @param {string} the body content
19813      */
19814     setContent: function(str)
19815     {
19816         this.html = str;
19817         if (this.contentEl) {
19818             this.contentEl.dom.innerHTML = str;
19819         }
19820         
19821     },
19822     // as it get's added to the bottom of the page.
19823     onRender : function(ct, position)
19824     {
19825         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19826         
19827         
19828         
19829         if(!this.el){
19830             var cfg = Roo.apply({},  this.getAutoCreate());
19831             cfg.id = Roo.id();
19832             
19833             if (this.cls) {
19834                 cfg.cls += ' ' + this.cls;
19835             }
19836             if (this.style) {
19837                 cfg.style = this.style;
19838             }
19839             //Roo.log("adding to ");
19840             this.el = Roo.get(document.body).createChild(cfg, position);
19841 //            Roo.log(this.el);
19842         }
19843         
19844         this.contentEl = this.el.select('.popover-content',true).first();
19845         this.headerEl =  this.el.select('.popover-title',true).first();
19846         
19847         var nitems = [];
19848         if(typeof(this.items) != 'undefined'){
19849             var items = this.items;
19850             delete this.items;
19851
19852             for(var i =0;i < items.length;i++) {
19853                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19854             }
19855         }
19856
19857         this.items = nitems;
19858         
19859         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19860         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19861         
19862         
19863         
19864         this.initEvents();
19865     },
19866     
19867     resizeMask : function()
19868     {
19869         this.maskEl.setSize(
19870             Roo.lib.Dom.getViewWidth(true),
19871             Roo.lib.Dom.getViewHeight(true)
19872         );
19873     },
19874     
19875     initEvents : function()
19876     {
19877         
19878         if (!this.modal) { 
19879             Roo.bootstrap.Popover.register(this);
19880         }
19881          
19882         this.arrowEl = this.el.select('.arrow',true).first();
19883         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19884         this.el.enableDisplayMode('block');
19885         this.el.hide();
19886  
19887         
19888         if (this.over === false && !this.parent()) {
19889             return; 
19890         }
19891         if (this.triggers === false) {
19892             return;
19893         }
19894          
19895         // support parent
19896         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19897         var triggers = this.trigger ? this.trigger.split(' ') : [];
19898         Roo.each(triggers, function(trigger) {
19899         
19900             if (trigger == 'click') {
19901                 on_el.on('click', this.toggle, this);
19902             } else if (trigger != 'manual') {
19903                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19904                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19905       
19906                 on_el.on(eventIn  ,this.enter, this);
19907                 on_el.on(eventOut, this.leave, this);
19908             }
19909         }, this);
19910     },
19911     
19912     
19913     // private
19914     timeout : null,
19915     hoverState : null,
19916     
19917     toggle : function () {
19918         this.hoverState == 'in' ? this.leave() : this.enter();
19919     },
19920     
19921     enter : function () {
19922         
19923         clearTimeout(this.timeout);
19924     
19925         this.hoverState = 'in';
19926     
19927         if (!this.delay || !this.delay.show) {
19928             this.show();
19929             return;
19930         }
19931         var _t = this;
19932         this.timeout = setTimeout(function () {
19933             if (_t.hoverState == 'in') {
19934                 _t.show();
19935             }
19936         }, this.delay.show)
19937     },
19938     
19939     leave : function() {
19940         clearTimeout(this.timeout);
19941     
19942         this.hoverState = 'out';
19943     
19944         if (!this.delay || !this.delay.hide) {
19945             this.hide();
19946             return;
19947         }
19948         var _t = this;
19949         this.timeout = setTimeout(function () {
19950             if (_t.hoverState == 'out') {
19951                 _t.hide();
19952             }
19953         }, this.delay.hide)
19954     },
19955     /**
19956      * Show the popover
19957      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19958      * @param {string} (left|right|top|bottom) position
19959      */
19960     show : function (on_el, placement)
19961     {
19962         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19963         on_el = on_el || false; // default to false
19964          
19965         if (!on_el) {
19966             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19967                 on_el = this.parent().el;
19968             } else if (this.over) {
19969                 Roo.get(this.over);
19970             }
19971             
19972         }
19973         
19974         this.alignEl = Roo.get( on_el );
19975
19976         if (!this.el) {
19977             this.render(document.body);
19978         }
19979         
19980         
19981          
19982         
19983         if (this.title === false) {
19984             this.headerEl.hide();
19985         }
19986         
19987        
19988         this.el.show();
19989         this.el.dom.style.display = 'block';
19990          
19991  
19992         if (this.alignEl) {
19993             this.updatePosition(this.placement, true);
19994              
19995         } else {
19996             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19997             var es = this.el.getSize();
19998             var x = Roo.lib.Dom.getViewWidth()/2;
19999             var y = Roo.lib.Dom.getViewHeight()/2;
20000             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20001             
20002         }
20003
20004         
20005         //var arrow = this.el.select('.arrow',true).first();
20006         //arrow.set(align[2], 
20007         
20008         this.el.addClass('in');
20009         
20010          
20011         
20012         this.hoverState = 'in';
20013         
20014         if (this.modal) {
20015             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20016             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20017             this.maskEl.dom.style.display = 'block';
20018             this.maskEl.addClass('show');
20019         }
20020         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20021  
20022         this.fireEvent('show', this);
20023         
20024     },
20025     /**
20026      * fire this manually after loading a grid in the table for example
20027      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20028      * @param {Boolean} try and move it if we cant get right position.
20029      */
20030     updatePosition : function(placement, try_move)
20031     {
20032         // allow for calling with no parameters
20033         placement = placement   ? placement :  this.placement;
20034         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20035         
20036         this.el.removeClass([
20037             'fade','top','bottom', 'left', 'right','in',
20038             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20039         ]);
20040         this.el.addClass(placement + ' bs-popover-' + placement);
20041         
20042         if (!this.alignEl ) {
20043             return false;
20044         }
20045         
20046         switch (placement) {
20047             case 'right':
20048                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20049                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20050                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20051                     //normal display... or moved up/down.
20052                     this.el.setXY(offset);
20053                     var xy = this.alignEl.getAnchorXY('tr', false);
20054                     xy[0]+=2;xy[1]+=5;
20055                     this.arrowEl.setXY(xy);
20056                     return true;
20057                 }
20058                 // continue through...
20059                 return this.updatePosition('left', false);
20060                 
20061             
20062             case 'left':
20063                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20064                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20065                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20066                     //normal display... or moved up/down.
20067                     this.el.setXY(offset);
20068                     var xy = this.alignEl.getAnchorXY('tl', false);
20069                     xy[0]-=10;xy[1]+=5; // << fix me
20070                     this.arrowEl.setXY(xy);
20071                     return true;
20072                 }
20073                 // call self...
20074                 return this.updatePosition('right', false);
20075             
20076             case 'top':
20077                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20078                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20079                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20080                     //normal display... or moved up/down.
20081                     this.el.setXY(offset);
20082                     var xy = this.alignEl.getAnchorXY('t', false);
20083                     xy[1]-=10; // << fix me
20084                     this.arrowEl.setXY(xy);
20085                     return true;
20086                 }
20087                 // fall through
20088                return this.updatePosition('bottom', false);
20089             
20090             case 'bottom':
20091                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20092                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20093                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20094                     //normal display... or moved up/down.
20095                     this.el.setXY(offset);
20096                     var xy = this.alignEl.getAnchorXY('b', false);
20097                      xy[1]+=2; // << fix me
20098                     this.arrowEl.setXY(xy);
20099                     return true;
20100                 }
20101                 // fall through
20102                 return this.updatePosition('top', false);
20103                 
20104             
20105         }
20106         
20107         
20108         return false;
20109     },
20110     
20111     hide : function()
20112     {
20113         this.el.setXY([0,0]);
20114         this.el.removeClass('in');
20115         this.el.hide();
20116         this.hoverState = null;
20117         this.maskEl.hide(); // always..
20118         this.fireEvent('hide', this);
20119     }
20120     
20121 });
20122
20123
20124 Roo.apply(Roo.bootstrap.Popover, {
20125
20126     alignment : {
20127         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20128         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20129         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20130         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20131     },
20132     
20133     zIndex : 20001,
20134
20135     clickHander : false,
20136     
20137
20138     onMouseDown : function(e)
20139     {
20140         if (!e.getTarget(".roo-popover")) {
20141             this.hideAll();
20142         }
20143          
20144     },
20145     
20146     popups : [],
20147     
20148     register : function(popup)
20149     {
20150         if (!Roo.bootstrap.Popover.clickHandler) {
20151             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20152         }
20153         // hide other popups.
20154         this.hideAll();
20155         this.popups.push(popup);
20156     },
20157     hideAll : function()
20158     {
20159         this.popups.forEach(function(p) {
20160             p.hide();
20161         });
20162     }
20163
20164 });/*
20165  * - LGPL
20166  *
20167  * Card header - holder for the card header elements.
20168  * 
20169  */
20170
20171 /**
20172  * @class Roo.bootstrap.PopoverNav
20173  * @extends Roo.bootstrap.NavGroup
20174  * Bootstrap Popover header navigation class
20175  * @constructor
20176  * Create a new Popover Header Navigation 
20177  * @param {Object} config The config object
20178  */
20179
20180 Roo.bootstrap.PopoverNav = function(config){
20181     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20182 };
20183
20184 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20185     
20186     
20187     container_method : 'getPopoverHeader' 
20188     
20189      
20190     
20191     
20192    
20193 });
20194
20195  
20196
20197  /*
20198  * - LGPL
20199  *
20200  * Progress
20201  * 
20202  */
20203
20204 /**
20205  * @class Roo.bootstrap.Progress
20206  * @extends Roo.bootstrap.Component
20207  * Bootstrap Progress class
20208  * @cfg {Boolean} striped striped of the progress bar
20209  * @cfg {Boolean} active animated of the progress bar
20210  * 
20211  * 
20212  * @constructor
20213  * Create a new Progress
20214  * @param {Object} config The config object
20215  */
20216
20217 Roo.bootstrap.Progress = function(config){
20218     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20219 };
20220
20221 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20222     
20223     striped : false,
20224     active: false,
20225     
20226     getAutoCreate : function(){
20227         var cfg = {
20228             tag: 'div',
20229             cls: 'progress'
20230         };
20231         
20232         
20233         if(this.striped){
20234             cfg.cls += ' progress-striped';
20235         }
20236       
20237         if(this.active){
20238             cfg.cls += ' active';
20239         }
20240         
20241         
20242         return cfg;
20243     }
20244    
20245 });
20246
20247  
20248
20249  /*
20250  * - LGPL
20251  *
20252  * ProgressBar
20253  * 
20254  */
20255
20256 /**
20257  * @class Roo.bootstrap.ProgressBar
20258  * @extends Roo.bootstrap.Component
20259  * Bootstrap ProgressBar class
20260  * @cfg {Number} aria_valuenow aria-value now
20261  * @cfg {Number} aria_valuemin aria-value min
20262  * @cfg {Number} aria_valuemax aria-value max
20263  * @cfg {String} label label for the progress bar
20264  * @cfg {String} panel (success | info | warning | danger )
20265  * @cfg {String} role role of the progress bar
20266  * @cfg {String} sr_only text
20267  * 
20268  * 
20269  * @constructor
20270  * Create a new ProgressBar
20271  * @param {Object} config The config object
20272  */
20273
20274 Roo.bootstrap.ProgressBar = function(config){
20275     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20276 };
20277
20278 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20279     
20280     aria_valuenow : 0,
20281     aria_valuemin : 0,
20282     aria_valuemax : 100,
20283     label : false,
20284     panel : false,
20285     role : false,
20286     sr_only: false,
20287     
20288     getAutoCreate : function()
20289     {
20290         
20291         var cfg = {
20292             tag: 'div',
20293             cls: 'progress-bar',
20294             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20295         };
20296         
20297         if(this.sr_only){
20298             cfg.cn = {
20299                 tag: 'span',
20300                 cls: 'sr-only',
20301                 html: this.sr_only
20302             }
20303         }
20304         
20305         if(this.role){
20306             cfg.role = this.role;
20307         }
20308         
20309         if(this.aria_valuenow){
20310             cfg['aria-valuenow'] = this.aria_valuenow;
20311         }
20312         
20313         if(this.aria_valuemin){
20314             cfg['aria-valuemin'] = this.aria_valuemin;
20315         }
20316         
20317         if(this.aria_valuemax){
20318             cfg['aria-valuemax'] = this.aria_valuemax;
20319         }
20320         
20321         if(this.label && !this.sr_only){
20322             cfg.html = this.label;
20323         }
20324         
20325         if(this.panel){
20326             cfg.cls += ' progress-bar-' + this.panel;
20327         }
20328         
20329         return cfg;
20330     },
20331     
20332     update : function(aria_valuenow)
20333     {
20334         this.aria_valuenow = aria_valuenow;
20335         
20336         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20337     }
20338    
20339 });
20340
20341  
20342
20343  /*
20344  * - LGPL
20345  *
20346  * column
20347  * 
20348  */
20349
20350 /**
20351  * @class Roo.bootstrap.TabGroup
20352  * @extends Roo.bootstrap.Column
20353  * Bootstrap Column class
20354  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20355  * @cfg {Boolean} carousel true to make the group behave like a carousel
20356  * @cfg {Boolean} bullets show bullets for the panels
20357  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20358  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20359  * @cfg {Boolean} showarrow (true|false) show arrow default true
20360  * 
20361  * @constructor
20362  * Create a new TabGroup
20363  * @param {Object} config The config object
20364  */
20365
20366 Roo.bootstrap.TabGroup = function(config){
20367     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20368     if (!this.navId) {
20369         this.navId = Roo.id();
20370     }
20371     this.tabs = [];
20372     Roo.bootstrap.TabGroup.register(this);
20373     
20374 };
20375
20376 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20377     
20378     carousel : false,
20379     transition : false,
20380     bullets : 0,
20381     timer : 0,
20382     autoslide : false,
20383     slideFn : false,
20384     slideOnTouch : false,
20385     showarrow : true,
20386     
20387     getAutoCreate : function()
20388     {
20389         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20390         
20391         cfg.cls += ' tab-content';
20392         
20393         if (this.carousel) {
20394             cfg.cls += ' carousel slide';
20395             
20396             cfg.cn = [{
20397                cls : 'carousel-inner',
20398                cn : []
20399             }];
20400         
20401             if(this.bullets  && !Roo.isTouch){
20402                 
20403                 var bullets = {
20404                     cls : 'carousel-bullets',
20405                     cn : []
20406                 };
20407                
20408                 if(this.bullets_cls){
20409                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20410                 }
20411                 
20412                 bullets.cn.push({
20413                     cls : 'clear'
20414                 });
20415                 
20416                 cfg.cn[0].cn.push(bullets);
20417             }
20418             
20419             if(this.showarrow){
20420                 cfg.cn[0].cn.push({
20421                     tag : 'div',
20422                     class : 'carousel-arrow',
20423                     cn : [
20424                         {
20425                             tag : 'div',
20426                             class : 'carousel-prev',
20427                             cn : [
20428                                 {
20429                                     tag : 'i',
20430                                     class : 'fa fa-chevron-left'
20431                                 }
20432                             ]
20433                         },
20434                         {
20435                             tag : 'div',
20436                             class : 'carousel-next',
20437                             cn : [
20438                                 {
20439                                     tag : 'i',
20440                                     class : 'fa fa-chevron-right'
20441                                 }
20442                             ]
20443                         }
20444                     ]
20445                 });
20446             }
20447             
20448         }
20449         
20450         return cfg;
20451     },
20452     
20453     initEvents:  function()
20454     {
20455 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20456 //            this.el.on("touchstart", this.onTouchStart, this);
20457 //        }
20458         
20459         if(this.autoslide){
20460             var _this = this;
20461             
20462             this.slideFn = window.setInterval(function() {
20463                 _this.showPanelNext();
20464             }, this.timer);
20465         }
20466         
20467         if(this.showarrow){
20468             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20469             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20470         }
20471         
20472         
20473     },
20474     
20475 //    onTouchStart : function(e, el, o)
20476 //    {
20477 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20478 //            return;
20479 //        }
20480 //        
20481 //        this.showPanelNext();
20482 //    },
20483     
20484     
20485     getChildContainer : function()
20486     {
20487         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20488     },
20489     
20490     /**
20491     * register a Navigation item
20492     * @param {Roo.bootstrap.NavItem} the navitem to add
20493     */
20494     register : function(item)
20495     {
20496         this.tabs.push( item);
20497         item.navId = this.navId; // not really needed..
20498         this.addBullet();
20499     
20500     },
20501     
20502     getActivePanel : function()
20503     {
20504         var r = false;
20505         Roo.each(this.tabs, function(t) {
20506             if (t.active) {
20507                 r = t;
20508                 return false;
20509             }
20510             return null;
20511         });
20512         return r;
20513         
20514     },
20515     getPanelByName : function(n)
20516     {
20517         var r = false;
20518         Roo.each(this.tabs, function(t) {
20519             if (t.tabId == n) {
20520                 r = t;
20521                 return false;
20522             }
20523             return null;
20524         });
20525         return r;
20526     },
20527     indexOfPanel : function(p)
20528     {
20529         var r = false;
20530         Roo.each(this.tabs, function(t,i) {
20531             if (t.tabId == p.tabId) {
20532                 r = i;
20533                 return false;
20534             }
20535             return null;
20536         });
20537         return r;
20538     },
20539     /**
20540      * show a specific panel
20541      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20542      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20543      */
20544     showPanel : function (pan)
20545     {
20546         if(this.transition || typeof(pan) == 'undefined'){
20547             Roo.log("waiting for the transitionend");
20548             return false;
20549         }
20550         
20551         if (typeof(pan) == 'number') {
20552             pan = this.tabs[pan];
20553         }
20554         
20555         if (typeof(pan) == 'string') {
20556             pan = this.getPanelByName(pan);
20557         }
20558         
20559         var cur = this.getActivePanel();
20560         
20561         if(!pan || !cur){
20562             Roo.log('pan or acitve pan is undefined');
20563             return false;
20564         }
20565         
20566         if (pan.tabId == this.getActivePanel().tabId) {
20567             return true;
20568         }
20569         
20570         if (false === cur.fireEvent('beforedeactivate')) {
20571             return false;
20572         }
20573         
20574         if(this.bullets > 0 && !Roo.isTouch){
20575             this.setActiveBullet(this.indexOfPanel(pan));
20576         }
20577         
20578         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20579             
20580             //class="carousel-item carousel-item-next carousel-item-left"
20581             
20582             this.transition = true;
20583             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20584             var lr = dir == 'next' ? 'left' : 'right';
20585             pan.el.addClass(dir); // or prev
20586             pan.el.addClass('carousel-item-' + dir); // or prev
20587             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20588             cur.el.addClass(lr); // or right
20589             pan.el.addClass(lr);
20590             cur.el.addClass('carousel-item-' +lr); // or right
20591             pan.el.addClass('carousel-item-' +lr);
20592             
20593             
20594             var _this = this;
20595             cur.el.on('transitionend', function() {
20596                 Roo.log("trans end?");
20597                 
20598                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20599                 pan.setActive(true);
20600                 
20601                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20602                 cur.setActive(false);
20603                 
20604                 _this.transition = false;
20605                 
20606             }, this, { single:  true } );
20607             
20608             return true;
20609         }
20610         
20611         cur.setActive(false);
20612         pan.setActive(true);
20613         
20614         return true;
20615         
20616     },
20617     showPanelNext : function()
20618     {
20619         var i = this.indexOfPanel(this.getActivePanel());
20620         
20621         if (i >= this.tabs.length - 1 && !this.autoslide) {
20622             return;
20623         }
20624         
20625         if (i >= this.tabs.length - 1 && this.autoslide) {
20626             i = -1;
20627         }
20628         
20629         this.showPanel(this.tabs[i+1]);
20630     },
20631     
20632     showPanelPrev : function()
20633     {
20634         var i = this.indexOfPanel(this.getActivePanel());
20635         
20636         if (i  < 1 && !this.autoslide) {
20637             return;
20638         }
20639         
20640         if (i < 1 && this.autoslide) {
20641             i = this.tabs.length;
20642         }
20643         
20644         this.showPanel(this.tabs[i-1]);
20645     },
20646     
20647     
20648     addBullet: function()
20649     {
20650         if(!this.bullets || Roo.isTouch){
20651             return;
20652         }
20653         var ctr = this.el.select('.carousel-bullets',true).first();
20654         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20655         var bullet = ctr.createChild({
20656             cls : 'bullet bullet-' + i
20657         },ctr.dom.lastChild);
20658         
20659         
20660         var _this = this;
20661         
20662         bullet.on('click', (function(e, el, o, ii, t){
20663
20664             e.preventDefault();
20665
20666             this.showPanel(ii);
20667
20668             if(this.autoslide && this.slideFn){
20669                 clearInterval(this.slideFn);
20670                 this.slideFn = window.setInterval(function() {
20671                     _this.showPanelNext();
20672                 }, this.timer);
20673             }
20674
20675         }).createDelegate(this, [i, bullet], true));
20676                 
20677         
20678     },
20679      
20680     setActiveBullet : function(i)
20681     {
20682         if(Roo.isTouch){
20683             return;
20684         }
20685         
20686         Roo.each(this.el.select('.bullet', true).elements, function(el){
20687             el.removeClass('selected');
20688         });
20689
20690         var bullet = this.el.select('.bullet-' + i, true).first();
20691         
20692         if(!bullet){
20693             return;
20694         }
20695         
20696         bullet.addClass('selected');
20697     }
20698     
20699     
20700   
20701 });
20702
20703  
20704
20705  
20706  
20707 Roo.apply(Roo.bootstrap.TabGroup, {
20708     
20709     groups: {},
20710      /**
20711     * register a Navigation Group
20712     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20713     */
20714     register : function(navgrp)
20715     {
20716         this.groups[navgrp.navId] = navgrp;
20717         
20718     },
20719     /**
20720     * fetch a Navigation Group based on the navigation ID
20721     * if one does not exist , it will get created.
20722     * @param {string} the navgroup to add
20723     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20724     */
20725     get: function(navId) {
20726         if (typeof(this.groups[navId]) == 'undefined') {
20727             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20728         }
20729         return this.groups[navId] ;
20730     }
20731     
20732     
20733     
20734 });
20735
20736  /*
20737  * - LGPL
20738  *
20739  * TabPanel
20740  * 
20741  */
20742
20743 /**
20744  * @class Roo.bootstrap.TabPanel
20745  * @extends Roo.bootstrap.Component
20746  * Bootstrap TabPanel class
20747  * @cfg {Boolean} active panel active
20748  * @cfg {String} html panel content
20749  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20750  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20751  * @cfg {String} href click to link..
20752  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20753  * 
20754  * 
20755  * @constructor
20756  * Create a new TabPanel
20757  * @param {Object} config The config object
20758  */
20759
20760 Roo.bootstrap.TabPanel = function(config){
20761     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20762     this.addEvents({
20763         /**
20764              * @event changed
20765              * Fires when the active status changes
20766              * @param {Roo.bootstrap.TabPanel} this
20767              * @param {Boolean} state the new state
20768             
20769          */
20770         'changed': true,
20771         /**
20772              * @event beforedeactivate
20773              * Fires before a tab is de-activated - can be used to do validation on a form.
20774              * @param {Roo.bootstrap.TabPanel} this
20775              * @return {Boolean} false if there is an error
20776             
20777          */
20778         'beforedeactivate': true
20779      });
20780     
20781     this.tabId = this.tabId || Roo.id();
20782   
20783 };
20784
20785 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20786     
20787     active: false,
20788     html: false,
20789     tabId: false,
20790     navId : false,
20791     href : '',
20792     touchSlide : false,
20793     getAutoCreate : function(){
20794         
20795         
20796         var cfg = {
20797             tag: 'div',
20798             // item is needed for carousel - not sure if it has any effect otherwise
20799             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20800             html: this.html || ''
20801         };
20802         
20803         if(this.active){
20804             cfg.cls += ' active';
20805         }
20806         
20807         if(this.tabId){
20808             cfg.tabId = this.tabId;
20809         }
20810         
20811         
20812         
20813         return cfg;
20814     },
20815     
20816     initEvents:  function()
20817     {
20818         var p = this.parent();
20819         
20820         this.navId = this.navId || p.navId;
20821         
20822         if (typeof(this.navId) != 'undefined') {
20823             // not really needed.. but just in case.. parent should be a NavGroup.
20824             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20825             
20826             tg.register(this);
20827             
20828             var i = tg.tabs.length - 1;
20829             
20830             if(this.active && tg.bullets > 0 && i < tg.bullets){
20831                 tg.setActiveBullet(i);
20832             }
20833         }
20834         
20835         this.el.on('click', this.onClick, this);
20836         
20837         if(Roo.isTouch && this.touchSlide){
20838             this.el.on("touchstart", this.onTouchStart, this);
20839             this.el.on("touchmove", this.onTouchMove, this);
20840             this.el.on("touchend", this.onTouchEnd, this);
20841         }
20842         
20843     },
20844     
20845     onRender : function(ct, position)
20846     {
20847         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20848     },
20849     
20850     setActive : function(state)
20851     {
20852         Roo.log("panel - set active " + this.tabId + "=" + state);
20853         
20854         this.active = state;
20855         if (!state) {
20856             this.el.removeClass('active');
20857             
20858         } else  if (!this.el.hasClass('active')) {
20859             this.el.addClass('active');
20860         }
20861         
20862         this.fireEvent('changed', this, state);
20863     },
20864     
20865     onClick : function(e)
20866     {
20867         e.preventDefault();
20868         
20869         if(!this.href.length){
20870             return;
20871         }
20872         
20873         window.location.href = this.href;
20874     },
20875     
20876     startX : 0,
20877     startY : 0,
20878     endX : 0,
20879     endY : 0,
20880     swiping : false,
20881     
20882     onTouchStart : function(e)
20883     {
20884         this.swiping = false;
20885         
20886         this.startX = e.browserEvent.touches[0].clientX;
20887         this.startY = e.browserEvent.touches[0].clientY;
20888     },
20889     
20890     onTouchMove : function(e)
20891     {
20892         this.swiping = true;
20893         
20894         this.endX = e.browserEvent.touches[0].clientX;
20895         this.endY = e.browserEvent.touches[0].clientY;
20896     },
20897     
20898     onTouchEnd : function(e)
20899     {
20900         if(!this.swiping){
20901             this.onClick(e);
20902             return;
20903         }
20904         
20905         var tabGroup = this.parent();
20906         
20907         if(this.endX > this.startX){ // swiping right
20908             tabGroup.showPanelPrev();
20909             return;
20910         }
20911         
20912         if(this.startX > this.endX){ // swiping left
20913             tabGroup.showPanelNext();
20914             return;
20915         }
20916     }
20917     
20918     
20919 });
20920  
20921
20922  
20923
20924  /*
20925  * - LGPL
20926  *
20927  * DateField
20928  * 
20929  */
20930
20931 /**
20932  * @class Roo.bootstrap.DateField
20933  * @extends Roo.bootstrap.Input
20934  * Bootstrap DateField class
20935  * @cfg {Number} weekStart default 0
20936  * @cfg {String} viewMode default empty, (months|years)
20937  * @cfg {String} minViewMode default empty, (months|years)
20938  * @cfg {Number} startDate default -Infinity
20939  * @cfg {Number} endDate default Infinity
20940  * @cfg {Boolean} todayHighlight default false
20941  * @cfg {Boolean} todayBtn default false
20942  * @cfg {Boolean} calendarWeeks default false
20943  * @cfg {Object} daysOfWeekDisabled default empty
20944  * @cfg {Boolean} singleMode default false (true | false)
20945  * 
20946  * @cfg {Boolean} keyboardNavigation default true
20947  * @cfg {String} language default en
20948  * 
20949  * @constructor
20950  * Create a new DateField
20951  * @param {Object} config The config object
20952  */
20953
20954 Roo.bootstrap.DateField = function(config){
20955     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20956      this.addEvents({
20957             /**
20958              * @event show
20959              * Fires when this field show.
20960              * @param {Roo.bootstrap.DateField} this
20961              * @param {Mixed} date The date value
20962              */
20963             show : true,
20964             /**
20965              * @event show
20966              * Fires when this field hide.
20967              * @param {Roo.bootstrap.DateField} this
20968              * @param {Mixed} date The date value
20969              */
20970             hide : true,
20971             /**
20972              * @event select
20973              * Fires when select a date.
20974              * @param {Roo.bootstrap.DateField} this
20975              * @param {Mixed} date The date value
20976              */
20977             select : true,
20978             /**
20979              * @event beforeselect
20980              * Fires when before select a date.
20981              * @param {Roo.bootstrap.DateField} this
20982              * @param {Mixed} date The date value
20983              */
20984             beforeselect : true
20985         });
20986 };
20987
20988 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20989     
20990     /**
20991      * @cfg {String} format
20992      * The default date format string which can be overriden for localization support.  The format must be
20993      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20994      */
20995     format : "m/d/y",
20996     /**
20997      * @cfg {String} altFormats
20998      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20999      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21000      */
21001     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21002     
21003     weekStart : 0,
21004     
21005     viewMode : '',
21006     
21007     minViewMode : '',
21008     
21009     todayHighlight : false,
21010     
21011     todayBtn: false,
21012     
21013     language: 'en',
21014     
21015     keyboardNavigation: true,
21016     
21017     calendarWeeks: false,
21018     
21019     startDate: -Infinity,
21020     
21021     endDate: Infinity,
21022     
21023     daysOfWeekDisabled: [],
21024     
21025     _events: [],
21026     
21027     singleMode : false,
21028     
21029     UTCDate: function()
21030     {
21031         return new Date(Date.UTC.apply(Date, arguments));
21032     },
21033     
21034     UTCToday: function()
21035     {
21036         var today = new Date();
21037         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21038     },
21039     
21040     getDate: function() {
21041             var d = this.getUTCDate();
21042             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21043     },
21044     
21045     getUTCDate: function() {
21046             return this.date;
21047     },
21048     
21049     setDate: function(d) {
21050             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21051     },
21052     
21053     setUTCDate: function(d) {
21054             this.date = d;
21055             this.setValue(this.formatDate(this.date));
21056     },
21057         
21058     onRender: function(ct, position)
21059     {
21060         
21061         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21062         
21063         this.language = this.language || 'en';
21064         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21065         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21066         
21067         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21068         this.format = this.format || 'm/d/y';
21069         this.isInline = false;
21070         this.isInput = true;
21071         this.component = this.el.select('.add-on', true).first() || false;
21072         this.component = (this.component && this.component.length === 0) ? false : this.component;
21073         this.hasInput = this.component && this.inputEl().length;
21074         
21075         if (typeof(this.minViewMode === 'string')) {
21076             switch (this.minViewMode) {
21077                 case 'months':
21078                     this.minViewMode = 1;
21079                     break;
21080                 case 'years':
21081                     this.minViewMode = 2;
21082                     break;
21083                 default:
21084                     this.minViewMode = 0;
21085                     break;
21086             }
21087         }
21088         
21089         if (typeof(this.viewMode === 'string')) {
21090             switch (this.viewMode) {
21091                 case 'months':
21092                     this.viewMode = 1;
21093                     break;
21094                 case 'years':
21095                     this.viewMode = 2;
21096                     break;
21097                 default:
21098                     this.viewMode = 0;
21099                     break;
21100             }
21101         }
21102                 
21103         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21104         
21105 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21106         
21107         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21108         
21109         this.picker().on('mousedown', this.onMousedown, this);
21110         this.picker().on('click', this.onClick, this);
21111         
21112         this.picker().addClass('datepicker-dropdown');
21113         
21114         this.startViewMode = this.viewMode;
21115         
21116         if(this.singleMode){
21117             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21118                 v.setVisibilityMode(Roo.Element.DISPLAY);
21119                 v.hide();
21120             });
21121             
21122             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21123                 v.setStyle('width', '189px');
21124             });
21125         }
21126         
21127         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21128             if(!this.calendarWeeks){
21129                 v.remove();
21130                 return;
21131             }
21132             
21133             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21134             v.attr('colspan', function(i, val){
21135                 return parseInt(val) + 1;
21136             });
21137         });
21138                         
21139         
21140         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21141         
21142         this.setStartDate(this.startDate);
21143         this.setEndDate(this.endDate);
21144         
21145         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21146         
21147         this.fillDow();
21148         this.fillMonths();
21149         this.update();
21150         this.showMode();
21151         
21152         if(this.isInline) {
21153             this.showPopup();
21154         }
21155     },
21156     
21157     picker : function()
21158     {
21159         return this.pickerEl;
21160 //        return this.el.select('.datepicker', true).first();
21161     },
21162     
21163     fillDow: function()
21164     {
21165         var dowCnt = this.weekStart;
21166         
21167         var dow = {
21168             tag: 'tr',
21169             cn: [
21170                 
21171             ]
21172         };
21173         
21174         if(this.calendarWeeks){
21175             dow.cn.push({
21176                 tag: 'th',
21177                 cls: 'cw',
21178                 html: '&nbsp;'
21179             })
21180         }
21181         
21182         while (dowCnt < this.weekStart + 7) {
21183             dow.cn.push({
21184                 tag: 'th',
21185                 cls: 'dow',
21186                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21187             });
21188         }
21189         
21190         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21191     },
21192     
21193     fillMonths: function()
21194     {    
21195         var i = 0;
21196         var months = this.picker().select('>.datepicker-months td', true).first();
21197         
21198         months.dom.innerHTML = '';
21199         
21200         while (i < 12) {
21201             var month = {
21202                 tag: 'span',
21203                 cls: 'month',
21204                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21205             };
21206             
21207             months.createChild(month);
21208         }
21209         
21210     },
21211     
21212     update: function()
21213     {
21214         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;
21215         
21216         if (this.date < this.startDate) {
21217             this.viewDate = new Date(this.startDate);
21218         } else if (this.date > this.endDate) {
21219             this.viewDate = new Date(this.endDate);
21220         } else {
21221             this.viewDate = new Date(this.date);
21222         }
21223         
21224         this.fill();
21225     },
21226     
21227     fill: function() 
21228     {
21229         var d = new Date(this.viewDate),
21230                 year = d.getUTCFullYear(),
21231                 month = d.getUTCMonth(),
21232                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21233                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21234                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21235                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21236                 currentDate = this.date && this.date.valueOf(),
21237                 today = this.UTCToday();
21238         
21239         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21240         
21241 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21242         
21243 //        this.picker.select('>tfoot th.today').
21244 //                                              .text(dates[this.language].today)
21245 //                                              .toggle(this.todayBtn !== false);
21246     
21247         this.updateNavArrows();
21248         this.fillMonths();
21249                                                 
21250         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21251         
21252         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21253          
21254         prevMonth.setUTCDate(day);
21255         
21256         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21257         
21258         var nextMonth = new Date(prevMonth);
21259         
21260         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21261         
21262         nextMonth = nextMonth.valueOf();
21263         
21264         var fillMonths = false;
21265         
21266         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21267         
21268         while(prevMonth.valueOf() <= nextMonth) {
21269             var clsName = '';
21270             
21271             if (prevMonth.getUTCDay() === this.weekStart) {
21272                 if(fillMonths){
21273                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21274                 }
21275                     
21276                 fillMonths = {
21277                     tag: 'tr',
21278                     cn: []
21279                 };
21280                 
21281                 if(this.calendarWeeks){
21282                     // ISO 8601: First week contains first thursday.
21283                     // ISO also states week starts on Monday, but we can be more abstract here.
21284                     var
21285                     // Start of current week: based on weekstart/current date
21286                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21287                     // Thursday of this week
21288                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21289                     // First Thursday of year, year from thursday
21290                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21291                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21292                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21293                     
21294                     fillMonths.cn.push({
21295                         tag: 'td',
21296                         cls: 'cw',
21297                         html: calWeek
21298                     });
21299                 }
21300             }
21301             
21302             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21303                 clsName += ' old';
21304             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21305                 clsName += ' new';
21306             }
21307             if (this.todayHighlight &&
21308                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21309                 prevMonth.getUTCMonth() == today.getMonth() &&
21310                 prevMonth.getUTCDate() == today.getDate()) {
21311                 clsName += ' today';
21312             }
21313             
21314             if (currentDate && prevMonth.valueOf() === currentDate) {
21315                 clsName += ' active';
21316             }
21317             
21318             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21319                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21320                     clsName += ' disabled';
21321             }
21322             
21323             fillMonths.cn.push({
21324                 tag: 'td',
21325                 cls: 'day ' + clsName,
21326                 html: prevMonth.getDate()
21327             });
21328             
21329             prevMonth.setDate(prevMonth.getDate()+1);
21330         }
21331           
21332         var currentYear = this.date && this.date.getUTCFullYear();
21333         var currentMonth = this.date && this.date.getUTCMonth();
21334         
21335         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21336         
21337         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21338             v.removeClass('active');
21339             
21340             if(currentYear === year && k === currentMonth){
21341                 v.addClass('active');
21342             }
21343             
21344             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21345                 v.addClass('disabled');
21346             }
21347             
21348         });
21349         
21350         
21351         year = parseInt(year/10, 10) * 10;
21352         
21353         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21354         
21355         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21356         
21357         year -= 1;
21358         for (var i = -1; i < 11; i++) {
21359             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21360                 tag: 'span',
21361                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21362                 html: year
21363             });
21364             
21365             year += 1;
21366         }
21367     },
21368     
21369     showMode: function(dir) 
21370     {
21371         if (dir) {
21372             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21373         }
21374         
21375         Roo.each(this.picker().select('>div',true).elements, function(v){
21376             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21377             v.hide();
21378         });
21379         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21380     },
21381     
21382     place: function()
21383     {
21384         if(this.isInline) {
21385             return;
21386         }
21387         
21388         this.picker().removeClass(['bottom', 'top']);
21389         
21390         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21391             /*
21392              * place to the top of element!
21393              *
21394              */
21395             
21396             this.picker().addClass('top');
21397             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21398             
21399             return;
21400         }
21401         
21402         this.picker().addClass('bottom');
21403         
21404         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21405     },
21406     
21407     parseDate : function(value)
21408     {
21409         if(!value || value instanceof Date){
21410             return value;
21411         }
21412         var v = Date.parseDate(value, this.format);
21413         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21414             v = Date.parseDate(value, 'Y-m-d');
21415         }
21416         if(!v && this.altFormats){
21417             if(!this.altFormatsArray){
21418                 this.altFormatsArray = this.altFormats.split("|");
21419             }
21420             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21421                 v = Date.parseDate(value, this.altFormatsArray[i]);
21422             }
21423         }
21424         return v;
21425     },
21426     
21427     formatDate : function(date, fmt)
21428     {   
21429         return (!date || !(date instanceof Date)) ?
21430         date : date.dateFormat(fmt || this.format);
21431     },
21432     
21433     onFocus : function()
21434     {
21435         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21436         this.showPopup();
21437     },
21438     
21439     onBlur : function()
21440     {
21441         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21442         
21443         var d = this.inputEl().getValue();
21444         
21445         this.setValue(d);
21446                 
21447         this.hidePopup();
21448     },
21449     
21450     showPopup : function()
21451     {
21452         this.picker().show();
21453         this.update();
21454         this.place();
21455         
21456         this.fireEvent('showpopup', this, this.date);
21457     },
21458     
21459     hidePopup : function()
21460     {
21461         if(this.isInline) {
21462             return;
21463         }
21464         this.picker().hide();
21465         this.viewMode = this.startViewMode;
21466         this.showMode();
21467         
21468         this.fireEvent('hidepopup', this, this.date);
21469         
21470     },
21471     
21472     onMousedown: function(e)
21473     {
21474         e.stopPropagation();
21475         e.preventDefault();
21476     },
21477     
21478     keyup: function(e)
21479     {
21480         Roo.bootstrap.DateField.superclass.keyup.call(this);
21481         this.update();
21482     },
21483
21484     setValue: function(v)
21485     {
21486         if(this.fireEvent('beforeselect', this, v) !== false){
21487             var d = new Date(this.parseDate(v) ).clearTime();
21488         
21489             if(isNaN(d.getTime())){
21490                 this.date = this.viewDate = '';
21491                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21492                 return;
21493             }
21494
21495             v = this.formatDate(d);
21496
21497             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21498
21499             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21500
21501             this.update();
21502
21503             this.fireEvent('select', this, this.date);
21504         }
21505     },
21506     
21507     getValue: function()
21508     {
21509         return this.formatDate(this.date);
21510     },
21511     
21512     fireKey: function(e)
21513     {
21514         if (!this.picker().isVisible()){
21515             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21516                 this.showPopup();
21517             }
21518             return;
21519         }
21520         
21521         var dateChanged = false,
21522         dir, day, month,
21523         newDate, newViewDate;
21524         
21525         switch(e.keyCode){
21526             case 27: // escape
21527                 this.hidePopup();
21528                 e.preventDefault();
21529                 break;
21530             case 37: // left
21531             case 39: // right
21532                 if (!this.keyboardNavigation) {
21533                     break;
21534                 }
21535                 dir = e.keyCode == 37 ? -1 : 1;
21536                 
21537                 if (e.ctrlKey){
21538                     newDate = this.moveYear(this.date, dir);
21539                     newViewDate = this.moveYear(this.viewDate, dir);
21540                 } else if (e.shiftKey){
21541                     newDate = this.moveMonth(this.date, dir);
21542                     newViewDate = this.moveMonth(this.viewDate, dir);
21543                 } else {
21544                     newDate = new Date(this.date);
21545                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21546                     newViewDate = new Date(this.viewDate);
21547                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21548                 }
21549                 if (this.dateWithinRange(newDate)){
21550                     this.date = newDate;
21551                     this.viewDate = newViewDate;
21552                     this.setValue(this.formatDate(this.date));
21553 //                    this.update();
21554                     e.preventDefault();
21555                     dateChanged = true;
21556                 }
21557                 break;
21558             case 38: // up
21559             case 40: // down
21560                 if (!this.keyboardNavigation) {
21561                     break;
21562                 }
21563                 dir = e.keyCode == 38 ? -1 : 1;
21564                 if (e.ctrlKey){
21565                     newDate = this.moveYear(this.date, dir);
21566                     newViewDate = this.moveYear(this.viewDate, dir);
21567                 } else if (e.shiftKey){
21568                     newDate = this.moveMonth(this.date, dir);
21569                     newViewDate = this.moveMonth(this.viewDate, dir);
21570                 } else {
21571                     newDate = new Date(this.date);
21572                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21573                     newViewDate = new Date(this.viewDate);
21574                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21575                 }
21576                 if (this.dateWithinRange(newDate)){
21577                     this.date = newDate;
21578                     this.viewDate = newViewDate;
21579                     this.setValue(this.formatDate(this.date));
21580 //                    this.update();
21581                     e.preventDefault();
21582                     dateChanged = true;
21583                 }
21584                 break;
21585             case 13: // enter
21586                 this.setValue(this.formatDate(this.date));
21587                 this.hidePopup();
21588                 e.preventDefault();
21589                 break;
21590             case 9: // tab
21591                 this.setValue(this.formatDate(this.date));
21592                 this.hidePopup();
21593                 break;
21594             case 16: // shift
21595             case 17: // ctrl
21596             case 18: // alt
21597                 break;
21598             default :
21599                 this.hidePopup();
21600                 
21601         }
21602     },
21603     
21604     
21605     onClick: function(e) 
21606     {
21607         e.stopPropagation();
21608         e.preventDefault();
21609         
21610         var target = e.getTarget();
21611         
21612         if(target.nodeName.toLowerCase() === 'i'){
21613             target = Roo.get(target).dom.parentNode;
21614         }
21615         
21616         var nodeName = target.nodeName;
21617         var className = target.className;
21618         var html = target.innerHTML;
21619         //Roo.log(nodeName);
21620         
21621         switch(nodeName.toLowerCase()) {
21622             case 'th':
21623                 switch(className) {
21624                     case 'switch':
21625                         this.showMode(1);
21626                         break;
21627                     case 'prev':
21628                     case 'next':
21629                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21630                         switch(this.viewMode){
21631                                 case 0:
21632                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21633                                         break;
21634                                 case 1:
21635                                 case 2:
21636                                         this.viewDate = this.moveYear(this.viewDate, dir);
21637                                         break;
21638                         }
21639                         this.fill();
21640                         break;
21641                     case 'today':
21642                         var date = new Date();
21643                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21644 //                        this.fill()
21645                         this.setValue(this.formatDate(this.date));
21646                         
21647                         this.hidePopup();
21648                         break;
21649                 }
21650                 break;
21651             case 'span':
21652                 if (className.indexOf('disabled') < 0) {
21653                     this.viewDate.setUTCDate(1);
21654                     if (className.indexOf('month') > -1) {
21655                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21656                     } else {
21657                         var year = parseInt(html, 10) || 0;
21658                         this.viewDate.setUTCFullYear(year);
21659                         
21660                     }
21661                     
21662                     if(this.singleMode){
21663                         this.setValue(this.formatDate(this.viewDate));
21664                         this.hidePopup();
21665                         return;
21666                     }
21667                     
21668                     this.showMode(-1);
21669                     this.fill();
21670                 }
21671                 break;
21672                 
21673             case 'td':
21674                 //Roo.log(className);
21675                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21676                     var day = parseInt(html, 10) || 1;
21677                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21678                         month = (this.viewDate || new Date()).getUTCMonth();
21679
21680                     if (className.indexOf('old') > -1) {
21681                         if(month === 0 ){
21682                             month = 11;
21683                             year -= 1;
21684                         }else{
21685                             month -= 1;
21686                         }
21687                     } else if (className.indexOf('new') > -1) {
21688                         if (month == 11) {
21689                             month = 0;
21690                             year += 1;
21691                         } else {
21692                             month += 1;
21693                         }
21694                     }
21695                     //Roo.log([year,month,day]);
21696                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21697                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21698 //                    this.fill();
21699                     //Roo.log(this.formatDate(this.date));
21700                     this.setValue(this.formatDate(this.date));
21701                     this.hidePopup();
21702                 }
21703                 break;
21704         }
21705     },
21706     
21707     setStartDate: function(startDate)
21708     {
21709         this.startDate = startDate || -Infinity;
21710         if (this.startDate !== -Infinity) {
21711             this.startDate = this.parseDate(this.startDate);
21712         }
21713         this.update();
21714         this.updateNavArrows();
21715     },
21716
21717     setEndDate: function(endDate)
21718     {
21719         this.endDate = endDate || Infinity;
21720         if (this.endDate !== Infinity) {
21721             this.endDate = this.parseDate(this.endDate);
21722         }
21723         this.update();
21724         this.updateNavArrows();
21725     },
21726     
21727     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21728     {
21729         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21730         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21731             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21732         }
21733         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21734             return parseInt(d, 10);
21735         });
21736         this.update();
21737         this.updateNavArrows();
21738     },
21739     
21740     updateNavArrows: function() 
21741     {
21742         if(this.singleMode){
21743             return;
21744         }
21745         
21746         var d = new Date(this.viewDate),
21747         year = d.getUTCFullYear(),
21748         month = d.getUTCMonth();
21749         
21750         Roo.each(this.picker().select('.prev', true).elements, function(v){
21751             v.show();
21752             switch (this.viewMode) {
21753                 case 0:
21754
21755                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21756                         v.hide();
21757                     }
21758                     break;
21759                 case 1:
21760                 case 2:
21761                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21762                         v.hide();
21763                     }
21764                     break;
21765             }
21766         });
21767         
21768         Roo.each(this.picker().select('.next', true).elements, function(v){
21769             v.show();
21770             switch (this.viewMode) {
21771                 case 0:
21772
21773                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21774                         v.hide();
21775                     }
21776                     break;
21777                 case 1:
21778                 case 2:
21779                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21780                         v.hide();
21781                     }
21782                     break;
21783             }
21784         })
21785     },
21786     
21787     moveMonth: function(date, dir)
21788     {
21789         if (!dir) {
21790             return date;
21791         }
21792         var new_date = new Date(date.valueOf()),
21793         day = new_date.getUTCDate(),
21794         month = new_date.getUTCMonth(),
21795         mag = Math.abs(dir),
21796         new_month, test;
21797         dir = dir > 0 ? 1 : -1;
21798         if (mag == 1){
21799             test = dir == -1
21800             // If going back one month, make sure month is not current month
21801             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21802             ? function(){
21803                 return new_date.getUTCMonth() == month;
21804             }
21805             // If going forward one month, make sure month is as expected
21806             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21807             : function(){
21808                 return new_date.getUTCMonth() != new_month;
21809             };
21810             new_month = month + dir;
21811             new_date.setUTCMonth(new_month);
21812             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21813             if (new_month < 0 || new_month > 11) {
21814                 new_month = (new_month + 12) % 12;
21815             }
21816         } else {
21817             // For magnitudes >1, move one month at a time...
21818             for (var i=0; i<mag; i++) {
21819                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21820                 new_date = this.moveMonth(new_date, dir);
21821             }
21822             // ...then reset the day, keeping it in the new month
21823             new_month = new_date.getUTCMonth();
21824             new_date.setUTCDate(day);
21825             test = function(){
21826                 return new_month != new_date.getUTCMonth();
21827             };
21828         }
21829         // Common date-resetting loop -- if date is beyond end of month, make it
21830         // end of month
21831         while (test()){
21832             new_date.setUTCDate(--day);
21833             new_date.setUTCMonth(new_month);
21834         }
21835         return new_date;
21836     },
21837
21838     moveYear: function(date, dir)
21839     {
21840         return this.moveMonth(date, dir*12);
21841     },
21842
21843     dateWithinRange: function(date)
21844     {
21845         return date >= this.startDate && date <= this.endDate;
21846     },
21847
21848     
21849     remove: function() 
21850     {
21851         this.picker().remove();
21852     },
21853     
21854     validateValue : function(value)
21855     {
21856         if(this.getVisibilityEl().hasClass('hidden')){
21857             return true;
21858         }
21859         
21860         if(value.length < 1)  {
21861             if(this.allowBlank){
21862                 return true;
21863             }
21864             return false;
21865         }
21866         
21867         if(value.length < this.minLength){
21868             return false;
21869         }
21870         if(value.length > this.maxLength){
21871             return false;
21872         }
21873         if(this.vtype){
21874             var vt = Roo.form.VTypes;
21875             if(!vt[this.vtype](value, this)){
21876                 return false;
21877             }
21878         }
21879         if(typeof this.validator == "function"){
21880             var msg = this.validator(value);
21881             if(msg !== true){
21882                 return false;
21883             }
21884         }
21885         
21886         if(this.regex && !this.regex.test(value)){
21887             return false;
21888         }
21889         
21890         if(typeof(this.parseDate(value)) == 'undefined'){
21891             return false;
21892         }
21893         
21894         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21895             return false;
21896         }      
21897         
21898         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21899             return false;
21900         } 
21901         
21902         
21903         return true;
21904     },
21905     
21906     reset : function()
21907     {
21908         this.date = this.viewDate = '';
21909         
21910         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21911     }
21912    
21913 });
21914
21915 Roo.apply(Roo.bootstrap.DateField,  {
21916     
21917     head : {
21918         tag: 'thead',
21919         cn: [
21920         {
21921             tag: 'tr',
21922             cn: [
21923             {
21924                 tag: 'th',
21925                 cls: 'prev',
21926                 html: '<i class="fa fa-arrow-left"/>'
21927             },
21928             {
21929                 tag: 'th',
21930                 cls: 'switch',
21931                 colspan: '5'
21932             },
21933             {
21934                 tag: 'th',
21935                 cls: 'next',
21936                 html: '<i class="fa fa-arrow-right"/>'
21937             }
21938
21939             ]
21940         }
21941         ]
21942     },
21943     
21944     content : {
21945         tag: 'tbody',
21946         cn: [
21947         {
21948             tag: 'tr',
21949             cn: [
21950             {
21951                 tag: 'td',
21952                 colspan: '7'
21953             }
21954             ]
21955         }
21956         ]
21957     },
21958     
21959     footer : {
21960         tag: 'tfoot',
21961         cn: [
21962         {
21963             tag: 'tr',
21964             cn: [
21965             {
21966                 tag: 'th',
21967                 colspan: '7',
21968                 cls: 'today'
21969             }
21970                     
21971             ]
21972         }
21973         ]
21974     },
21975     
21976     dates:{
21977         en: {
21978             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21979             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21980             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21981             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21982             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21983             today: "Today"
21984         }
21985     },
21986     
21987     modes: [
21988     {
21989         clsName: 'days',
21990         navFnc: 'Month',
21991         navStep: 1
21992     },
21993     {
21994         clsName: 'months',
21995         navFnc: 'FullYear',
21996         navStep: 1
21997     },
21998     {
21999         clsName: 'years',
22000         navFnc: 'FullYear',
22001         navStep: 10
22002     }]
22003 });
22004
22005 Roo.apply(Roo.bootstrap.DateField,  {
22006   
22007     template : {
22008         tag: 'div',
22009         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22010         cn: [
22011         {
22012             tag: 'div',
22013             cls: 'datepicker-days',
22014             cn: [
22015             {
22016                 tag: 'table',
22017                 cls: 'table-condensed',
22018                 cn:[
22019                 Roo.bootstrap.DateField.head,
22020                 {
22021                     tag: 'tbody'
22022                 },
22023                 Roo.bootstrap.DateField.footer
22024                 ]
22025             }
22026             ]
22027         },
22028         {
22029             tag: 'div',
22030             cls: 'datepicker-months',
22031             cn: [
22032             {
22033                 tag: 'table',
22034                 cls: 'table-condensed',
22035                 cn:[
22036                 Roo.bootstrap.DateField.head,
22037                 Roo.bootstrap.DateField.content,
22038                 Roo.bootstrap.DateField.footer
22039                 ]
22040             }
22041             ]
22042         },
22043         {
22044             tag: 'div',
22045             cls: 'datepicker-years',
22046             cn: [
22047             {
22048                 tag: 'table',
22049                 cls: 'table-condensed',
22050                 cn:[
22051                 Roo.bootstrap.DateField.head,
22052                 Roo.bootstrap.DateField.content,
22053                 Roo.bootstrap.DateField.footer
22054                 ]
22055             }
22056             ]
22057         }
22058         ]
22059     }
22060 });
22061
22062  
22063
22064  /*
22065  * - LGPL
22066  *
22067  * TimeField
22068  * 
22069  */
22070
22071 /**
22072  * @class Roo.bootstrap.TimeField
22073  * @extends Roo.bootstrap.Input
22074  * Bootstrap DateField class
22075  * 
22076  * 
22077  * @constructor
22078  * Create a new TimeField
22079  * @param {Object} config The config object
22080  */
22081
22082 Roo.bootstrap.TimeField = function(config){
22083     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22084     this.addEvents({
22085             /**
22086              * @event show
22087              * Fires when this field show.
22088              * @param {Roo.bootstrap.DateField} thisthis
22089              * @param {Mixed} date The date value
22090              */
22091             show : true,
22092             /**
22093              * @event show
22094              * Fires when this field hide.
22095              * @param {Roo.bootstrap.DateField} this
22096              * @param {Mixed} date The date value
22097              */
22098             hide : true,
22099             /**
22100              * @event select
22101              * Fires when select a date.
22102              * @param {Roo.bootstrap.DateField} this
22103              * @param {Mixed} date The date value
22104              */
22105             select : true
22106         });
22107 };
22108
22109 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22110     
22111     /**
22112      * @cfg {String} format
22113      * The default time format string which can be overriden for localization support.  The format must be
22114      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22115      */
22116     format : "H:i",
22117
22118     getAutoCreate : function()
22119     {
22120         this.after = '<i class="fa far fa-clock"></i>';
22121         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22122         
22123          
22124     },
22125     onRender: function(ct, position)
22126     {
22127         
22128         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22129                 
22130         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22131         
22132         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22133         
22134         this.pop = this.picker().select('>.datepicker-time',true).first();
22135         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22136         
22137         this.picker().on('mousedown', this.onMousedown, this);
22138         this.picker().on('click', this.onClick, this);
22139         
22140         this.picker().addClass('datepicker-dropdown');
22141     
22142         this.fillTime();
22143         this.update();
22144             
22145         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22146         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22147         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22148         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22149         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22150         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22151
22152     },
22153     
22154     fireKey: function(e){
22155         if (!this.picker().isVisible()){
22156             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22157                 this.show();
22158             }
22159             return;
22160         }
22161
22162         e.preventDefault();
22163         
22164         switch(e.keyCode){
22165             case 27: // escape
22166                 this.hide();
22167                 break;
22168             case 37: // left
22169             case 39: // right
22170                 this.onTogglePeriod();
22171                 break;
22172             case 38: // up
22173                 this.onIncrementMinutes();
22174                 break;
22175             case 40: // down
22176                 this.onDecrementMinutes();
22177                 break;
22178             case 13: // enter
22179             case 9: // tab
22180                 this.setTime();
22181                 break;
22182         }
22183     },
22184     
22185     onClick: function(e) {
22186         e.stopPropagation();
22187         e.preventDefault();
22188     },
22189     
22190     picker : function()
22191     {
22192         return this.pickerEl;
22193     },
22194     
22195     fillTime: function()
22196     {    
22197         var time = this.pop.select('tbody', true).first();
22198         
22199         time.dom.innerHTML = '';
22200         
22201         time.createChild({
22202             tag: 'tr',
22203             cn: [
22204                 {
22205                     tag: 'td',
22206                     cn: [
22207                         {
22208                             tag: 'a',
22209                             href: '#',
22210                             cls: 'btn',
22211                             cn: [
22212                                 {
22213                                     tag: 'i',
22214                                     cls: 'hours-up fa fas fa-chevron-up'
22215                                 }
22216                             ]
22217                         } 
22218                     ]
22219                 },
22220                 {
22221                     tag: 'td',
22222                     cls: 'separator'
22223                 },
22224                 {
22225                     tag: 'td',
22226                     cn: [
22227                         {
22228                             tag: 'a',
22229                             href: '#',
22230                             cls: 'btn',
22231                             cn: [
22232                                 {
22233                                     tag: 'i',
22234                                     cls: 'minutes-up fa fas fa-chevron-up'
22235                                 }
22236                             ]
22237                         }
22238                     ]
22239                 },
22240                 {
22241                     tag: 'td',
22242                     cls: 'separator'
22243                 }
22244             ]
22245         });
22246         
22247         time.createChild({
22248             tag: 'tr',
22249             cn: [
22250                 {
22251                     tag: 'td',
22252                     cn: [
22253                         {
22254                             tag: 'span',
22255                             cls: 'timepicker-hour',
22256                             html: '00'
22257                         }  
22258                     ]
22259                 },
22260                 {
22261                     tag: 'td',
22262                     cls: 'separator',
22263                     html: ':'
22264                 },
22265                 {
22266                     tag: 'td',
22267                     cn: [
22268                         {
22269                             tag: 'span',
22270                             cls: 'timepicker-minute',
22271                             html: '00'
22272                         }  
22273                     ]
22274                 },
22275                 {
22276                     tag: 'td',
22277                     cls: 'separator'
22278                 },
22279                 {
22280                     tag: 'td',
22281                     cn: [
22282                         {
22283                             tag: 'button',
22284                             type: 'button',
22285                             cls: 'btn btn-primary period',
22286                             html: 'AM'
22287                             
22288                         }
22289                     ]
22290                 }
22291             ]
22292         });
22293         
22294         time.createChild({
22295             tag: 'tr',
22296             cn: [
22297                 {
22298                     tag: 'td',
22299                     cn: [
22300                         {
22301                             tag: 'a',
22302                             href: '#',
22303                             cls: 'btn',
22304                             cn: [
22305                                 {
22306                                     tag: 'span',
22307                                     cls: 'hours-down fa fas fa-chevron-down'
22308                                 }
22309                             ]
22310                         }
22311                     ]
22312                 },
22313                 {
22314                     tag: 'td',
22315                     cls: 'separator'
22316                 },
22317                 {
22318                     tag: 'td',
22319                     cn: [
22320                         {
22321                             tag: 'a',
22322                             href: '#',
22323                             cls: 'btn',
22324                             cn: [
22325                                 {
22326                                     tag: 'span',
22327                                     cls: 'minutes-down fa fas fa-chevron-down'
22328                                 }
22329                             ]
22330                         }
22331                     ]
22332                 },
22333                 {
22334                     tag: 'td',
22335                     cls: 'separator'
22336                 }
22337             ]
22338         });
22339         
22340     },
22341     
22342     update: function()
22343     {
22344         
22345         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22346         
22347         this.fill();
22348     },
22349     
22350     fill: function() 
22351     {
22352         var hours = this.time.getHours();
22353         var minutes = this.time.getMinutes();
22354         var period = 'AM';
22355         
22356         if(hours > 11){
22357             period = 'PM';
22358         }
22359         
22360         if(hours == 0){
22361             hours = 12;
22362         }
22363         
22364         
22365         if(hours > 12){
22366             hours = hours - 12;
22367         }
22368         
22369         if(hours < 10){
22370             hours = '0' + hours;
22371         }
22372         
22373         if(minutes < 10){
22374             minutes = '0' + minutes;
22375         }
22376         
22377         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22378         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22379         this.pop.select('button', true).first().dom.innerHTML = period;
22380         
22381     },
22382     
22383     place: function()
22384     {   
22385         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22386         
22387         var cls = ['bottom'];
22388         
22389         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22390             cls.pop();
22391             cls.push('top');
22392         }
22393         
22394         cls.push('right');
22395         
22396         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22397             cls.pop();
22398             cls.push('left');
22399         }
22400         //this.picker().setXY(20000,20000);
22401         this.picker().addClass(cls.join('-'));
22402         
22403         var _this = this;
22404         
22405         Roo.each(cls, function(c){
22406             if(c == 'bottom'){
22407                 (function() {
22408                  //  
22409                 }).defer(200);
22410                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22411                 //_this.picker().setTop(_this.inputEl().getHeight());
22412                 return;
22413             }
22414             if(c == 'top'){
22415                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22416                 
22417                 //_this.picker().setTop(0 - _this.picker().getHeight());
22418                 return;
22419             }
22420             /*
22421             if(c == 'left'){
22422                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22423                 return;
22424             }
22425             if(c == 'right'){
22426                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22427                 return;
22428             }
22429             */
22430         });
22431         
22432     },
22433   
22434     onFocus : function()
22435     {
22436         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22437         this.show();
22438     },
22439     
22440     onBlur : function()
22441     {
22442         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22443         this.hide();
22444     },
22445     
22446     show : function()
22447     {
22448         this.picker().show();
22449         this.pop.show();
22450         this.update();
22451         this.place();
22452         
22453         this.fireEvent('show', this, this.date);
22454     },
22455     
22456     hide : function()
22457     {
22458         this.picker().hide();
22459         this.pop.hide();
22460         
22461         this.fireEvent('hide', this, this.date);
22462     },
22463     
22464     setTime : function()
22465     {
22466         this.hide();
22467         this.setValue(this.time.format(this.format));
22468         
22469         this.fireEvent('select', this, this.date);
22470         
22471         
22472     },
22473     
22474     onMousedown: function(e){
22475         e.stopPropagation();
22476         e.preventDefault();
22477     },
22478     
22479     onIncrementHours: function()
22480     {
22481         Roo.log('onIncrementHours');
22482         this.time = this.time.add(Date.HOUR, 1);
22483         this.update();
22484         
22485     },
22486     
22487     onDecrementHours: function()
22488     {
22489         Roo.log('onDecrementHours');
22490         this.time = this.time.add(Date.HOUR, -1);
22491         this.update();
22492     },
22493     
22494     onIncrementMinutes: function()
22495     {
22496         Roo.log('onIncrementMinutes');
22497         this.time = this.time.add(Date.MINUTE, 1);
22498         this.update();
22499     },
22500     
22501     onDecrementMinutes: function()
22502     {
22503         Roo.log('onDecrementMinutes');
22504         this.time = this.time.add(Date.MINUTE, -1);
22505         this.update();
22506     },
22507     
22508     onTogglePeriod: function()
22509     {
22510         Roo.log('onTogglePeriod');
22511         this.time = this.time.add(Date.HOUR, 12);
22512         this.update();
22513     }
22514     
22515    
22516 });
22517  
22518
22519 Roo.apply(Roo.bootstrap.TimeField,  {
22520   
22521     template : {
22522         tag: 'div',
22523         cls: 'datepicker dropdown-menu',
22524         cn: [
22525             {
22526                 tag: 'div',
22527                 cls: 'datepicker-time',
22528                 cn: [
22529                 {
22530                     tag: 'table',
22531                     cls: 'table-condensed',
22532                     cn:[
22533                         {
22534                             tag: 'tbody',
22535                             cn: [
22536                                 {
22537                                     tag: 'tr',
22538                                     cn: [
22539                                     {
22540                                         tag: 'td',
22541                                         colspan: '7'
22542                                     }
22543                                     ]
22544                                 }
22545                             ]
22546                         },
22547                         {
22548                             tag: 'tfoot',
22549                             cn: [
22550                                 {
22551                                     tag: 'tr',
22552                                     cn: [
22553                                     {
22554                                         tag: 'th',
22555                                         colspan: '7',
22556                                         cls: '',
22557                                         cn: [
22558                                             {
22559                                                 tag: 'button',
22560                                                 cls: 'btn btn-info ok',
22561                                                 html: 'OK'
22562                                             }
22563                                         ]
22564                                     }
22565                     
22566                                     ]
22567                                 }
22568                             ]
22569                         }
22570                     ]
22571                 }
22572                 ]
22573             }
22574         ]
22575     }
22576 });
22577
22578  
22579
22580  /*
22581  * - LGPL
22582  *
22583  * MonthField
22584  * 
22585  */
22586
22587 /**
22588  * @class Roo.bootstrap.MonthField
22589  * @extends Roo.bootstrap.Input
22590  * Bootstrap MonthField class
22591  * 
22592  * @cfg {String} language default en
22593  * 
22594  * @constructor
22595  * Create a new MonthField
22596  * @param {Object} config The config object
22597  */
22598
22599 Roo.bootstrap.MonthField = function(config){
22600     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22601     
22602     this.addEvents({
22603         /**
22604          * @event show
22605          * Fires when this field show.
22606          * @param {Roo.bootstrap.MonthField} this
22607          * @param {Mixed} date The date value
22608          */
22609         show : true,
22610         /**
22611          * @event show
22612          * Fires when this field hide.
22613          * @param {Roo.bootstrap.MonthField} this
22614          * @param {Mixed} date The date value
22615          */
22616         hide : true,
22617         /**
22618          * @event select
22619          * Fires when select a date.
22620          * @param {Roo.bootstrap.MonthField} this
22621          * @param {String} oldvalue The old value
22622          * @param {String} newvalue The new value
22623          */
22624         select : true
22625     });
22626 };
22627
22628 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22629     
22630     onRender: function(ct, position)
22631     {
22632         
22633         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22634         
22635         this.language = this.language || 'en';
22636         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22637         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22638         
22639         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22640         this.isInline = false;
22641         this.isInput = true;
22642         this.component = this.el.select('.add-on', true).first() || false;
22643         this.component = (this.component && this.component.length === 0) ? false : this.component;
22644         this.hasInput = this.component && this.inputEL().length;
22645         
22646         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22647         
22648         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22649         
22650         this.picker().on('mousedown', this.onMousedown, this);
22651         this.picker().on('click', this.onClick, this);
22652         
22653         this.picker().addClass('datepicker-dropdown');
22654         
22655         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22656             v.setStyle('width', '189px');
22657         });
22658         
22659         this.fillMonths();
22660         
22661         this.update();
22662         
22663         if(this.isInline) {
22664             this.show();
22665         }
22666         
22667     },
22668     
22669     setValue: function(v, suppressEvent)
22670     {   
22671         var o = this.getValue();
22672         
22673         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22674         
22675         this.update();
22676
22677         if(suppressEvent !== true){
22678             this.fireEvent('select', this, o, v);
22679         }
22680         
22681     },
22682     
22683     getValue: function()
22684     {
22685         return this.value;
22686     },
22687     
22688     onClick: function(e) 
22689     {
22690         e.stopPropagation();
22691         e.preventDefault();
22692         
22693         var target = e.getTarget();
22694         
22695         if(target.nodeName.toLowerCase() === 'i'){
22696             target = Roo.get(target).dom.parentNode;
22697         }
22698         
22699         var nodeName = target.nodeName;
22700         var className = target.className;
22701         var html = target.innerHTML;
22702         
22703         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22704             return;
22705         }
22706         
22707         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22708         
22709         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22710         
22711         this.hide();
22712                         
22713     },
22714     
22715     picker : function()
22716     {
22717         return this.pickerEl;
22718     },
22719     
22720     fillMonths: function()
22721     {    
22722         var i = 0;
22723         var months = this.picker().select('>.datepicker-months td', true).first();
22724         
22725         months.dom.innerHTML = '';
22726         
22727         while (i < 12) {
22728             var month = {
22729                 tag: 'span',
22730                 cls: 'month',
22731                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22732             };
22733             
22734             months.createChild(month);
22735         }
22736         
22737     },
22738     
22739     update: function()
22740     {
22741         var _this = this;
22742         
22743         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22744             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22745         }
22746         
22747         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22748             e.removeClass('active');
22749             
22750             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22751                 e.addClass('active');
22752             }
22753         })
22754     },
22755     
22756     place: function()
22757     {
22758         if(this.isInline) {
22759             return;
22760         }
22761         
22762         this.picker().removeClass(['bottom', 'top']);
22763         
22764         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22765             /*
22766              * place to the top of element!
22767              *
22768              */
22769             
22770             this.picker().addClass('top');
22771             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22772             
22773             return;
22774         }
22775         
22776         this.picker().addClass('bottom');
22777         
22778         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22779     },
22780     
22781     onFocus : function()
22782     {
22783         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22784         this.show();
22785     },
22786     
22787     onBlur : function()
22788     {
22789         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22790         
22791         var d = this.inputEl().getValue();
22792         
22793         this.setValue(d);
22794                 
22795         this.hide();
22796     },
22797     
22798     show : function()
22799     {
22800         this.picker().show();
22801         this.picker().select('>.datepicker-months', true).first().show();
22802         this.update();
22803         this.place();
22804         
22805         this.fireEvent('show', this, this.date);
22806     },
22807     
22808     hide : function()
22809     {
22810         if(this.isInline) {
22811             return;
22812         }
22813         this.picker().hide();
22814         this.fireEvent('hide', this, this.date);
22815         
22816     },
22817     
22818     onMousedown: function(e)
22819     {
22820         e.stopPropagation();
22821         e.preventDefault();
22822     },
22823     
22824     keyup: function(e)
22825     {
22826         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22827         this.update();
22828     },
22829
22830     fireKey: function(e)
22831     {
22832         if (!this.picker().isVisible()){
22833             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22834                 this.show();
22835             }
22836             return;
22837         }
22838         
22839         var dir;
22840         
22841         switch(e.keyCode){
22842             case 27: // escape
22843                 this.hide();
22844                 e.preventDefault();
22845                 break;
22846             case 37: // left
22847             case 39: // right
22848                 dir = e.keyCode == 37 ? -1 : 1;
22849                 
22850                 this.vIndex = this.vIndex + dir;
22851                 
22852                 if(this.vIndex < 0){
22853                     this.vIndex = 0;
22854                 }
22855                 
22856                 if(this.vIndex > 11){
22857                     this.vIndex = 11;
22858                 }
22859                 
22860                 if(isNaN(this.vIndex)){
22861                     this.vIndex = 0;
22862                 }
22863                 
22864                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22865                 
22866                 break;
22867             case 38: // up
22868             case 40: // down
22869                 
22870                 dir = e.keyCode == 38 ? -1 : 1;
22871                 
22872                 this.vIndex = this.vIndex + dir * 4;
22873                 
22874                 if(this.vIndex < 0){
22875                     this.vIndex = 0;
22876                 }
22877                 
22878                 if(this.vIndex > 11){
22879                     this.vIndex = 11;
22880                 }
22881                 
22882                 if(isNaN(this.vIndex)){
22883                     this.vIndex = 0;
22884                 }
22885                 
22886                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22887                 break;
22888                 
22889             case 13: // enter
22890                 
22891                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22892                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22893                 }
22894                 
22895                 this.hide();
22896                 e.preventDefault();
22897                 break;
22898             case 9: // tab
22899                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22900                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22901                 }
22902                 this.hide();
22903                 break;
22904             case 16: // shift
22905             case 17: // ctrl
22906             case 18: // alt
22907                 break;
22908             default :
22909                 this.hide();
22910                 
22911         }
22912     },
22913     
22914     remove: function() 
22915     {
22916         this.picker().remove();
22917     }
22918    
22919 });
22920
22921 Roo.apply(Roo.bootstrap.MonthField,  {
22922     
22923     content : {
22924         tag: 'tbody',
22925         cn: [
22926         {
22927             tag: 'tr',
22928             cn: [
22929             {
22930                 tag: 'td',
22931                 colspan: '7'
22932             }
22933             ]
22934         }
22935         ]
22936     },
22937     
22938     dates:{
22939         en: {
22940             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22941             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22942         }
22943     }
22944 });
22945
22946 Roo.apply(Roo.bootstrap.MonthField,  {
22947   
22948     template : {
22949         tag: 'div',
22950         cls: 'datepicker dropdown-menu roo-dynamic',
22951         cn: [
22952             {
22953                 tag: 'div',
22954                 cls: 'datepicker-months',
22955                 cn: [
22956                 {
22957                     tag: 'table',
22958                     cls: 'table-condensed',
22959                     cn:[
22960                         Roo.bootstrap.DateField.content
22961                     ]
22962                 }
22963                 ]
22964             }
22965         ]
22966     }
22967 });
22968
22969  
22970
22971  
22972  /*
22973  * - LGPL
22974  *
22975  * CheckBox
22976  * 
22977  */
22978
22979 /**
22980  * @class Roo.bootstrap.CheckBox
22981  * @extends Roo.bootstrap.Input
22982  * Bootstrap CheckBox class
22983  * 
22984  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22985  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22986  * @cfg {String} boxLabel The text that appears beside the checkbox
22987  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22988  * @cfg {Boolean} checked initnal the element
22989  * @cfg {Boolean} inline inline the element (default false)
22990  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22991  * @cfg {String} tooltip label tooltip
22992  * 
22993  * @constructor
22994  * Create a new CheckBox
22995  * @param {Object} config The config object
22996  */
22997
22998 Roo.bootstrap.CheckBox = function(config){
22999     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23000    
23001     this.addEvents({
23002         /**
23003         * @event check
23004         * Fires when the element is checked or unchecked.
23005         * @param {Roo.bootstrap.CheckBox} this This input
23006         * @param {Boolean} checked The new checked value
23007         */
23008        check : true,
23009        /**
23010         * @event click
23011         * Fires when the element is click.
23012         * @param {Roo.bootstrap.CheckBox} this This input
23013         */
23014        click : true
23015     });
23016     
23017 };
23018
23019 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23020   
23021     inputType: 'checkbox',
23022     inputValue: 1,
23023     valueOff: 0,
23024     boxLabel: false,
23025     checked: false,
23026     weight : false,
23027     inline: false,
23028     tooltip : '',
23029     
23030     // checkbox success does not make any sense really.. 
23031     invalidClass : "",
23032     validClass : "",
23033     
23034     
23035     getAutoCreate : function()
23036     {
23037         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23038         
23039         var id = Roo.id();
23040         
23041         var cfg = {};
23042         
23043         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23044         
23045         if(this.inline){
23046             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23047         }
23048         
23049         var input =  {
23050             tag: 'input',
23051             id : id,
23052             type : this.inputType,
23053             value : this.inputValue,
23054             cls : 'roo-' + this.inputType, //'form-box',
23055             placeholder : this.placeholder || ''
23056             
23057         };
23058         
23059         if(this.inputType != 'radio'){
23060             var hidden =  {
23061                 tag: 'input',
23062                 type : 'hidden',
23063                 cls : 'roo-hidden-value',
23064                 value : this.checked ? this.inputValue : this.valueOff
23065             };
23066         }
23067         
23068             
23069         if (this.weight) { // Validity check?
23070             cfg.cls += " " + this.inputType + "-" + this.weight;
23071         }
23072         
23073         if (this.disabled) {
23074             input.disabled=true;
23075         }
23076         
23077         if(this.checked){
23078             input.checked = this.checked;
23079         }
23080         
23081         if (this.name) {
23082             
23083             input.name = this.name;
23084             
23085             if(this.inputType != 'radio'){
23086                 hidden.name = this.name;
23087                 input.name = '_hidden_' + this.name;
23088             }
23089         }
23090         
23091         if (this.size) {
23092             input.cls += ' input-' + this.size;
23093         }
23094         
23095         var settings=this;
23096         
23097         ['xs','sm','md','lg'].map(function(size){
23098             if (settings[size]) {
23099                 cfg.cls += ' col-' + size + '-' + settings[size];
23100             }
23101         });
23102         
23103         var inputblock = input;
23104          
23105         if (this.before || this.after) {
23106             
23107             inputblock = {
23108                 cls : 'input-group',
23109                 cn :  [] 
23110             };
23111             
23112             if (this.before) {
23113                 inputblock.cn.push({
23114                     tag :'span',
23115                     cls : 'input-group-addon',
23116                     html : this.before
23117                 });
23118             }
23119             
23120             inputblock.cn.push(input);
23121             
23122             if(this.inputType != 'radio'){
23123                 inputblock.cn.push(hidden);
23124             }
23125             
23126             if (this.after) {
23127                 inputblock.cn.push({
23128                     tag :'span',
23129                     cls : 'input-group-addon',
23130                     html : this.after
23131                 });
23132             }
23133             
23134         }
23135         var boxLabelCfg = false;
23136         
23137         if(this.boxLabel){
23138            
23139             boxLabelCfg = {
23140                 tag: 'label',
23141                 //'for': id, // box label is handled by onclick - so no for...
23142                 cls: 'box-label',
23143                 html: this.boxLabel
23144             };
23145             if(this.tooltip){
23146                 boxLabelCfg.tooltip = this.tooltip;
23147             }
23148              
23149         }
23150         
23151         
23152         if (align ==='left' && this.fieldLabel.length) {
23153 //                Roo.log("left and has label");
23154             cfg.cn = [
23155                 {
23156                     tag: 'label',
23157                     'for' :  id,
23158                     cls : 'control-label',
23159                     html : this.fieldLabel
23160                 },
23161                 {
23162                     cls : "", 
23163                     cn: [
23164                         inputblock
23165                     ]
23166                 }
23167             ];
23168             
23169             if (boxLabelCfg) {
23170                 cfg.cn[1].cn.push(boxLabelCfg);
23171             }
23172             
23173             if(this.labelWidth > 12){
23174                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23175             }
23176             
23177             if(this.labelWidth < 13 && this.labelmd == 0){
23178                 this.labelmd = this.labelWidth;
23179             }
23180             
23181             if(this.labellg > 0){
23182                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23183                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23184             }
23185             
23186             if(this.labelmd > 0){
23187                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23188                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23189             }
23190             
23191             if(this.labelsm > 0){
23192                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23193                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23194             }
23195             
23196             if(this.labelxs > 0){
23197                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23198                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23199             }
23200             
23201         } else if ( this.fieldLabel.length) {
23202 //                Roo.log(" label");
23203                 cfg.cn = [
23204                    
23205                     {
23206                         tag: this.boxLabel ? 'span' : 'label',
23207                         'for': id,
23208                         cls: 'control-label box-input-label',
23209                         //cls : 'input-group-addon',
23210                         html : this.fieldLabel
23211                     },
23212                     
23213                     inputblock
23214                     
23215                 ];
23216                 if (boxLabelCfg) {
23217                     cfg.cn.push(boxLabelCfg);
23218                 }
23219
23220         } else {
23221             
23222 //                Roo.log(" no label && no align");
23223                 cfg.cn = [  inputblock ] ;
23224                 if (boxLabelCfg) {
23225                     cfg.cn.push(boxLabelCfg);
23226                 }
23227
23228                 
23229         }
23230         
23231        
23232         
23233         if(this.inputType != 'radio'){
23234             cfg.cn.push(hidden);
23235         }
23236         
23237         return cfg;
23238         
23239     },
23240     
23241     /**
23242      * return the real input element.
23243      */
23244     inputEl: function ()
23245     {
23246         return this.el.select('input.roo-' + this.inputType,true).first();
23247     },
23248     hiddenEl: function ()
23249     {
23250         return this.el.select('input.roo-hidden-value',true).first();
23251     },
23252     
23253     labelEl: function()
23254     {
23255         return this.el.select('label.control-label',true).first();
23256     },
23257     /* depricated... */
23258     
23259     label: function()
23260     {
23261         return this.labelEl();
23262     },
23263     
23264     boxLabelEl: function()
23265     {
23266         return this.el.select('label.box-label',true).first();
23267     },
23268     
23269     initEvents : function()
23270     {
23271 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23272         
23273         this.inputEl().on('click', this.onClick,  this);
23274         
23275         if (this.boxLabel) { 
23276             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23277         }
23278         
23279         this.startValue = this.getValue();
23280         
23281         if(this.groupId){
23282             Roo.bootstrap.CheckBox.register(this);
23283         }
23284     },
23285     
23286     onClick : function(e)
23287     {   
23288         if(this.fireEvent('click', this, e) !== false){
23289             this.setChecked(!this.checked);
23290         }
23291         
23292     },
23293     
23294     setChecked : function(state,suppressEvent)
23295     {
23296         this.startValue = this.getValue();
23297
23298         if(this.inputType == 'radio'){
23299             
23300             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23301                 e.dom.checked = false;
23302             });
23303             
23304             this.inputEl().dom.checked = true;
23305             
23306             this.inputEl().dom.value = this.inputValue;
23307             
23308             if(suppressEvent !== true){
23309                 this.fireEvent('check', this, true);
23310             }
23311             
23312             this.validate();
23313             
23314             return;
23315         }
23316         
23317         this.checked = state;
23318         
23319         this.inputEl().dom.checked = state;
23320         
23321         
23322         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23323         
23324         if(suppressEvent !== true){
23325             this.fireEvent('check', this, state);
23326         }
23327         
23328         this.validate();
23329     },
23330     
23331     getValue : function()
23332     {
23333         if(this.inputType == 'radio'){
23334             return this.getGroupValue();
23335         }
23336         
23337         return this.hiddenEl().dom.value;
23338         
23339     },
23340     
23341     getGroupValue : function()
23342     {
23343         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23344             return '';
23345         }
23346         
23347         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23348     },
23349     
23350     setValue : function(v,suppressEvent)
23351     {
23352         if(this.inputType == 'radio'){
23353             this.setGroupValue(v, suppressEvent);
23354             return;
23355         }
23356         
23357         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23358         
23359         this.validate();
23360     },
23361     
23362     setGroupValue : function(v, suppressEvent)
23363     {
23364         this.startValue = this.getValue();
23365         
23366         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23367             e.dom.checked = false;
23368             
23369             if(e.dom.value == v){
23370                 e.dom.checked = true;
23371             }
23372         });
23373         
23374         if(suppressEvent !== true){
23375             this.fireEvent('check', this, true);
23376         }
23377
23378         this.validate();
23379         
23380         return;
23381     },
23382     
23383     validate : function()
23384     {
23385         if(this.getVisibilityEl().hasClass('hidden')){
23386             return true;
23387         }
23388         
23389         if(
23390                 this.disabled || 
23391                 (this.inputType == 'radio' && this.validateRadio()) ||
23392                 (this.inputType == 'checkbox' && this.validateCheckbox())
23393         ){
23394             this.markValid();
23395             return true;
23396         }
23397         
23398         this.markInvalid();
23399         return false;
23400     },
23401     
23402     validateRadio : function()
23403     {
23404         if(this.getVisibilityEl().hasClass('hidden')){
23405             return true;
23406         }
23407         
23408         if(this.allowBlank){
23409             return true;
23410         }
23411         
23412         var valid = false;
23413         
23414         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23415             if(!e.dom.checked){
23416                 return;
23417             }
23418             
23419             valid = true;
23420             
23421             return false;
23422         });
23423         
23424         return valid;
23425     },
23426     
23427     validateCheckbox : function()
23428     {
23429         if(!this.groupId){
23430             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23431             //return (this.getValue() == this.inputValue) ? true : false;
23432         }
23433         
23434         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23435         
23436         if(!group){
23437             return false;
23438         }
23439         
23440         var r = false;
23441         
23442         for(var i in group){
23443             if(group[i].el.isVisible(true)){
23444                 r = false;
23445                 break;
23446             }
23447             
23448             r = true;
23449         }
23450         
23451         for(var i in group){
23452             if(r){
23453                 break;
23454             }
23455             
23456             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23457         }
23458         
23459         return r;
23460     },
23461     
23462     /**
23463      * Mark this field as valid
23464      */
23465     markValid : function()
23466     {
23467         var _this = this;
23468         
23469         this.fireEvent('valid', this);
23470         
23471         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23472         
23473         if(this.groupId){
23474             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23475         }
23476         
23477         if(label){
23478             label.markValid();
23479         }
23480
23481         if(this.inputType == 'radio'){
23482             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23483                 var fg = e.findParent('.form-group', false, true);
23484                 if (Roo.bootstrap.version == 3) {
23485                     fg.removeClass([_this.invalidClass, _this.validClass]);
23486                     fg.addClass(_this.validClass);
23487                 } else {
23488                     fg.removeClass(['is-valid', 'is-invalid']);
23489                     fg.addClass('is-valid');
23490                 }
23491             });
23492             
23493             return;
23494         }
23495
23496         if(!this.groupId){
23497             var fg = this.el.findParent('.form-group', false, true);
23498             if (Roo.bootstrap.version == 3) {
23499                 fg.removeClass([this.invalidClass, this.validClass]);
23500                 fg.addClass(this.validClass);
23501             } else {
23502                 fg.removeClass(['is-valid', 'is-invalid']);
23503                 fg.addClass('is-valid');
23504             }
23505             return;
23506         }
23507         
23508         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23509         
23510         if(!group){
23511             return;
23512         }
23513         
23514         for(var i in group){
23515             var fg = group[i].el.findParent('.form-group', false, true);
23516             if (Roo.bootstrap.version == 3) {
23517                 fg.removeClass([this.invalidClass, this.validClass]);
23518                 fg.addClass(this.validClass);
23519             } else {
23520                 fg.removeClass(['is-valid', 'is-invalid']);
23521                 fg.addClass('is-valid');
23522             }
23523         }
23524     },
23525     
23526      /**
23527      * Mark this field as invalid
23528      * @param {String} msg The validation message
23529      */
23530     markInvalid : function(msg)
23531     {
23532         if(this.allowBlank){
23533             return;
23534         }
23535         
23536         var _this = this;
23537         
23538         this.fireEvent('invalid', this, msg);
23539         
23540         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23541         
23542         if(this.groupId){
23543             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23544         }
23545         
23546         if(label){
23547             label.markInvalid();
23548         }
23549             
23550         if(this.inputType == 'radio'){
23551             
23552             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23553                 var fg = e.findParent('.form-group', false, true);
23554                 if (Roo.bootstrap.version == 3) {
23555                     fg.removeClass([_this.invalidClass, _this.validClass]);
23556                     fg.addClass(_this.invalidClass);
23557                 } else {
23558                     fg.removeClass(['is-invalid', 'is-valid']);
23559                     fg.addClass('is-invalid');
23560                 }
23561             });
23562             
23563             return;
23564         }
23565         
23566         if(!this.groupId){
23567             var fg = this.el.findParent('.form-group', false, true);
23568             if (Roo.bootstrap.version == 3) {
23569                 fg.removeClass([_this.invalidClass, _this.validClass]);
23570                 fg.addClass(_this.invalidClass);
23571             } else {
23572                 fg.removeClass(['is-invalid', 'is-valid']);
23573                 fg.addClass('is-invalid');
23574             }
23575             return;
23576         }
23577         
23578         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23579         
23580         if(!group){
23581             return;
23582         }
23583         
23584         for(var i in group){
23585             var fg = group[i].el.findParent('.form-group', false, true);
23586             if (Roo.bootstrap.version == 3) {
23587                 fg.removeClass([_this.invalidClass, _this.validClass]);
23588                 fg.addClass(_this.invalidClass);
23589             } else {
23590                 fg.removeClass(['is-invalid', 'is-valid']);
23591                 fg.addClass('is-invalid');
23592             }
23593         }
23594         
23595     },
23596     
23597     clearInvalid : function()
23598     {
23599         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23600         
23601         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23602         
23603         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23604         
23605         if (label && label.iconEl) {
23606             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23607             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23608         }
23609     },
23610     
23611     disable : function()
23612     {
23613         if(this.inputType != 'radio'){
23614             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23615             return;
23616         }
23617         
23618         var _this = this;
23619         
23620         if(this.rendered){
23621             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23622                 _this.getActionEl().addClass(this.disabledClass);
23623                 e.dom.disabled = true;
23624             });
23625         }
23626         
23627         this.disabled = true;
23628         this.fireEvent("disable", this);
23629         return this;
23630     },
23631
23632     enable : function()
23633     {
23634         if(this.inputType != 'radio'){
23635             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23636             return;
23637         }
23638         
23639         var _this = this;
23640         
23641         if(this.rendered){
23642             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23643                 _this.getActionEl().removeClass(this.disabledClass);
23644                 e.dom.disabled = false;
23645             });
23646         }
23647         
23648         this.disabled = false;
23649         this.fireEvent("enable", this);
23650         return this;
23651     },
23652     
23653     setBoxLabel : function(v)
23654     {
23655         this.boxLabel = v;
23656         
23657         if(this.rendered){
23658             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23659         }
23660     }
23661
23662 });
23663
23664 Roo.apply(Roo.bootstrap.CheckBox, {
23665     
23666     groups: {},
23667     
23668      /**
23669     * register a CheckBox Group
23670     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23671     */
23672     register : function(checkbox)
23673     {
23674         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23675             this.groups[checkbox.groupId] = {};
23676         }
23677         
23678         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23679             return;
23680         }
23681         
23682         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23683         
23684     },
23685     /**
23686     * fetch a CheckBox Group based on the group ID
23687     * @param {string} the group ID
23688     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23689     */
23690     get: function(groupId) {
23691         if (typeof(this.groups[groupId]) == 'undefined') {
23692             return false;
23693         }
23694         
23695         return this.groups[groupId] ;
23696     }
23697     
23698     
23699 });
23700 /*
23701  * - LGPL
23702  *
23703  * RadioItem
23704  * 
23705  */
23706
23707 /**
23708  * @class Roo.bootstrap.Radio
23709  * @extends Roo.bootstrap.Component
23710  * Bootstrap Radio class
23711  * @cfg {String} boxLabel - the label associated
23712  * @cfg {String} value - the value of radio
23713  * 
23714  * @constructor
23715  * Create a new Radio
23716  * @param {Object} config The config object
23717  */
23718 Roo.bootstrap.Radio = function(config){
23719     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23720     
23721 };
23722
23723 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23724     
23725     boxLabel : '',
23726     
23727     value : '',
23728     
23729     getAutoCreate : function()
23730     {
23731         var cfg = {
23732             tag : 'div',
23733             cls : 'form-group radio',
23734             cn : [
23735                 {
23736                     tag : 'label',
23737                     cls : 'box-label',
23738                     html : this.boxLabel
23739                 }
23740             ]
23741         };
23742         
23743         return cfg;
23744     },
23745     
23746     initEvents : function() 
23747     {
23748         this.parent().register(this);
23749         
23750         this.el.on('click', this.onClick, this);
23751         
23752     },
23753     
23754     onClick : function(e)
23755     {
23756         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23757             this.setChecked(true);
23758         }
23759     },
23760     
23761     setChecked : function(state, suppressEvent)
23762     {
23763         this.parent().setValue(this.value, suppressEvent);
23764         
23765     },
23766     
23767     setBoxLabel : function(v)
23768     {
23769         this.boxLabel = v;
23770         
23771         if(this.rendered){
23772             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23773         }
23774     }
23775     
23776 });
23777  
23778
23779  /*
23780  * - LGPL
23781  *
23782  * Input
23783  * 
23784  */
23785
23786 /**
23787  * @class Roo.bootstrap.SecurePass
23788  * @extends Roo.bootstrap.Input
23789  * Bootstrap SecurePass class
23790  *
23791  * 
23792  * @constructor
23793  * Create a new SecurePass
23794  * @param {Object} config The config object
23795  */
23796  
23797 Roo.bootstrap.SecurePass = function (config) {
23798     // these go here, so the translation tool can replace them..
23799     this.errors = {
23800         PwdEmpty: "Please type a password, and then retype it to confirm.",
23801         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23802         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23803         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23804         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23805         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23806         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23807         TooWeak: "Your password is Too Weak."
23808     },
23809     this.meterLabel = "Password strength:";
23810     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23811     this.meterClass = [
23812         "roo-password-meter-tooweak", 
23813         "roo-password-meter-weak", 
23814         "roo-password-meter-medium", 
23815         "roo-password-meter-strong", 
23816         "roo-password-meter-grey"
23817     ];
23818     
23819     this.errors = {};
23820     
23821     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23822 }
23823
23824 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23825     /**
23826      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23827      * {
23828      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23829      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23830      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23831      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23832      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23833      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23834      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23835      * })
23836      */
23837     // private
23838     
23839     meterWidth: 300,
23840     errorMsg :'',    
23841     errors: false,
23842     imageRoot: '/',
23843     /**
23844      * @cfg {String/Object} Label for the strength meter (defaults to
23845      * 'Password strength:')
23846      */
23847     // private
23848     meterLabel: '',
23849     /**
23850      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23851      * ['Weak', 'Medium', 'Strong'])
23852      */
23853     // private    
23854     pwdStrengths: false,    
23855     // private
23856     strength: 0,
23857     // private
23858     _lastPwd: null,
23859     // private
23860     kCapitalLetter: 0,
23861     kSmallLetter: 1,
23862     kDigit: 2,
23863     kPunctuation: 3,
23864     
23865     insecure: false,
23866     // private
23867     initEvents: function ()
23868     {
23869         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23870
23871         if (this.el.is('input[type=password]') && Roo.isSafari) {
23872             this.el.on('keydown', this.SafariOnKeyDown, this);
23873         }
23874
23875         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23876     },
23877     // private
23878     onRender: function (ct, position)
23879     {
23880         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23881         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23882         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23883
23884         this.trigger.createChild({
23885                    cn: [
23886                     {
23887                     //id: 'PwdMeter',
23888                     tag: 'div',
23889                     cls: 'roo-password-meter-grey col-xs-12',
23890                     style: {
23891                         //width: 0,
23892                         //width: this.meterWidth + 'px'                                                
23893                         }
23894                     },
23895                     {                            
23896                          cls: 'roo-password-meter-text'                          
23897                     }
23898                 ]            
23899         });
23900
23901          
23902         if (this.hideTrigger) {
23903             this.trigger.setDisplayed(false);
23904         }
23905         this.setSize(this.width || '', this.height || '');
23906     },
23907     // private
23908     onDestroy: function ()
23909     {
23910         if (this.trigger) {
23911             this.trigger.removeAllListeners();
23912             this.trigger.remove();
23913         }
23914         if (this.wrap) {
23915             this.wrap.remove();
23916         }
23917         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23918     },
23919     // private
23920     checkStrength: function ()
23921     {
23922         var pwd = this.inputEl().getValue();
23923         if (pwd == this._lastPwd) {
23924             return;
23925         }
23926
23927         var strength;
23928         if (this.ClientSideStrongPassword(pwd)) {
23929             strength = 3;
23930         } else if (this.ClientSideMediumPassword(pwd)) {
23931             strength = 2;
23932         } else if (this.ClientSideWeakPassword(pwd)) {
23933             strength = 1;
23934         } else {
23935             strength = 0;
23936         }
23937         
23938         Roo.log('strength1: ' + strength);
23939         
23940         //var pm = this.trigger.child('div/div/div').dom;
23941         var pm = this.trigger.child('div/div');
23942         pm.removeClass(this.meterClass);
23943         pm.addClass(this.meterClass[strength]);
23944                 
23945         
23946         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23947                 
23948         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23949         
23950         this._lastPwd = pwd;
23951     },
23952     reset: function ()
23953     {
23954         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23955         
23956         this._lastPwd = '';
23957         
23958         var pm = this.trigger.child('div/div');
23959         pm.removeClass(this.meterClass);
23960         pm.addClass('roo-password-meter-grey');        
23961         
23962         
23963         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23964         
23965         pt.innerHTML = '';
23966         this.inputEl().dom.type='password';
23967     },
23968     // private
23969     validateValue: function (value)
23970     {
23971         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23972             return false;
23973         }
23974         if (value.length == 0) {
23975             if (this.allowBlank) {
23976                 this.clearInvalid();
23977                 return true;
23978             }
23979
23980             this.markInvalid(this.errors.PwdEmpty);
23981             this.errorMsg = this.errors.PwdEmpty;
23982             return false;
23983         }
23984         
23985         if(this.insecure){
23986             return true;
23987         }
23988         
23989         if (!value.match(/[\x21-\x7e]+/)) {
23990             this.markInvalid(this.errors.PwdBadChar);
23991             this.errorMsg = this.errors.PwdBadChar;
23992             return false;
23993         }
23994         if (value.length < 6) {
23995             this.markInvalid(this.errors.PwdShort);
23996             this.errorMsg = this.errors.PwdShort;
23997             return false;
23998         }
23999         if (value.length > 16) {
24000             this.markInvalid(this.errors.PwdLong);
24001             this.errorMsg = this.errors.PwdLong;
24002             return false;
24003         }
24004         var strength;
24005         if (this.ClientSideStrongPassword(value)) {
24006             strength = 3;
24007         } else if (this.ClientSideMediumPassword(value)) {
24008             strength = 2;
24009         } else if (this.ClientSideWeakPassword(value)) {
24010             strength = 1;
24011         } else {
24012             strength = 0;
24013         }
24014
24015         
24016         if (strength < 2) {
24017             //this.markInvalid(this.errors.TooWeak);
24018             this.errorMsg = this.errors.TooWeak;
24019             //return false;
24020         }
24021         
24022         
24023         console.log('strength2: ' + strength);
24024         
24025         //var pm = this.trigger.child('div/div/div').dom;
24026         
24027         var pm = this.trigger.child('div/div');
24028         pm.removeClass(this.meterClass);
24029         pm.addClass(this.meterClass[strength]);
24030                 
24031         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24032                 
24033         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24034         
24035         this.errorMsg = ''; 
24036         return true;
24037     },
24038     // private
24039     CharacterSetChecks: function (type)
24040     {
24041         this.type = type;
24042         this.fResult = false;
24043     },
24044     // private
24045     isctype: function (character, type)
24046     {
24047         switch (type) {  
24048             case this.kCapitalLetter:
24049                 if (character >= 'A' && character <= 'Z') {
24050                     return true;
24051                 }
24052                 break;
24053             
24054             case this.kSmallLetter:
24055                 if (character >= 'a' && character <= 'z') {
24056                     return true;
24057                 }
24058                 break;
24059             
24060             case this.kDigit:
24061                 if (character >= '0' && character <= '9') {
24062                     return true;
24063                 }
24064                 break;
24065             
24066             case this.kPunctuation:
24067                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24068                     return true;
24069                 }
24070                 break;
24071             
24072             default:
24073                 return false;
24074         }
24075
24076     },
24077     // private
24078     IsLongEnough: function (pwd, size)
24079     {
24080         return !(pwd == null || isNaN(size) || pwd.length < size);
24081     },
24082     // private
24083     SpansEnoughCharacterSets: function (word, nb)
24084     {
24085         if (!this.IsLongEnough(word, nb))
24086         {
24087             return false;
24088         }
24089
24090         var characterSetChecks = new Array(
24091             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24092             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24093         );
24094         
24095         for (var index = 0; index < word.length; ++index) {
24096             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24097                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24098                     characterSetChecks[nCharSet].fResult = true;
24099                     break;
24100                 }
24101             }
24102         }
24103
24104         var nCharSets = 0;
24105         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24106             if (characterSetChecks[nCharSet].fResult) {
24107                 ++nCharSets;
24108             }
24109         }
24110
24111         if (nCharSets < nb) {
24112             return false;
24113         }
24114         return true;
24115     },
24116     // private
24117     ClientSideStrongPassword: function (pwd)
24118     {
24119         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24120     },
24121     // private
24122     ClientSideMediumPassword: function (pwd)
24123     {
24124         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24125     },
24126     // private
24127     ClientSideWeakPassword: function (pwd)
24128     {
24129         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24130     }
24131           
24132 })//<script type="text/javascript">
24133
24134 /*
24135  * Based  Ext JS Library 1.1.1
24136  * Copyright(c) 2006-2007, Ext JS, LLC.
24137  * LGPL
24138  *
24139  */
24140  
24141 /**
24142  * @class Roo.HtmlEditorCore
24143  * @extends Roo.Component
24144  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24145  *
24146  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24147  */
24148
24149 Roo.HtmlEditorCore = function(config){
24150     
24151     
24152     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24153     
24154     
24155     this.addEvents({
24156         /**
24157          * @event initialize
24158          * Fires when the editor is fully initialized (including the iframe)
24159          * @param {Roo.HtmlEditorCore} this
24160          */
24161         initialize: true,
24162         /**
24163          * @event activate
24164          * Fires when the editor is first receives the focus. Any insertion must wait
24165          * until after this event.
24166          * @param {Roo.HtmlEditorCore} this
24167          */
24168         activate: true,
24169          /**
24170          * @event beforesync
24171          * Fires before the textarea is updated with content from the editor iframe. Return false
24172          * to cancel the sync.
24173          * @param {Roo.HtmlEditorCore} this
24174          * @param {String} html
24175          */
24176         beforesync: true,
24177          /**
24178          * @event beforepush
24179          * Fires before the iframe editor is updated with content from the textarea. Return false
24180          * to cancel the push.
24181          * @param {Roo.HtmlEditorCore} this
24182          * @param {String} html
24183          */
24184         beforepush: true,
24185          /**
24186          * @event sync
24187          * Fires when the textarea is updated with content from the editor iframe.
24188          * @param {Roo.HtmlEditorCore} this
24189          * @param {String} html
24190          */
24191         sync: true,
24192          /**
24193          * @event push
24194          * Fires when the iframe editor is updated with content from the textarea.
24195          * @param {Roo.HtmlEditorCore} this
24196          * @param {String} html
24197          */
24198         push: true,
24199         
24200         /**
24201          * @event editorevent
24202          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24203          * @param {Roo.HtmlEditorCore} this
24204          */
24205         editorevent: true
24206         
24207     });
24208     
24209     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24210     
24211     // defaults : white / black...
24212     this.applyBlacklists();
24213     
24214     
24215     
24216 };
24217
24218
24219 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24220
24221
24222      /**
24223      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24224      */
24225     
24226     owner : false,
24227     
24228      /**
24229      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24230      *                        Roo.resizable.
24231      */
24232     resizable : false,
24233      /**
24234      * @cfg {Number} height (in pixels)
24235      */   
24236     height: 300,
24237    /**
24238      * @cfg {Number} width (in pixels)
24239      */   
24240     width: 500,
24241     
24242     /**
24243      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24244      * 
24245      */
24246     stylesheets: false,
24247     
24248     // id of frame..
24249     frameId: false,
24250     
24251     // private properties
24252     validationEvent : false,
24253     deferHeight: true,
24254     initialized : false,
24255     activated : false,
24256     sourceEditMode : false,
24257     onFocus : Roo.emptyFn,
24258     iframePad:3,
24259     hideMode:'offsets',
24260     
24261     clearUp: true,
24262     
24263     // blacklist + whitelisted elements..
24264     black: false,
24265     white: false,
24266      
24267     bodyCls : '',
24268
24269     /**
24270      * Protected method that will not generally be called directly. It
24271      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24272      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24273      */
24274     getDocMarkup : function(){
24275         // body styles..
24276         var st = '';
24277         
24278         // inherit styels from page...?? 
24279         if (this.stylesheets === false) {
24280             
24281             Roo.get(document.head).select('style').each(function(node) {
24282                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24283             });
24284             
24285             Roo.get(document.head).select('link').each(function(node) { 
24286                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24287             });
24288             
24289         } else if (!this.stylesheets.length) {
24290                 // simple..
24291                 st = '<style type="text/css">' +
24292                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24293                    '</style>';
24294         } else {
24295             for (var i in this.stylesheets) { 
24296                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24297             }
24298             
24299         }
24300         
24301         st +=  '<style type="text/css">' +
24302             'IMG { cursor: pointer } ' +
24303         '</style>';
24304
24305         var cls = 'roo-htmleditor-body';
24306         
24307         if(this.bodyCls.length){
24308             cls += ' ' + this.bodyCls;
24309         }
24310         
24311         return '<html><head>' + st  +
24312             //<style type="text/css">' +
24313             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24314             //'</style>' +
24315             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24316     },
24317
24318     // private
24319     onRender : function(ct, position)
24320     {
24321         var _t = this;
24322         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24323         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24324         
24325         
24326         this.el.dom.style.border = '0 none';
24327         this.el.dom.setAttribute('tabIndex', -1);
24328         this.el.addClass('x-hidden hide');
24329         
24330         
24331         
24332         if(Roo.isIE){ // fix IE 1px bogus margin
24333             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24334         }
24335        
24336         
24337         this.frameId = Roo.id();
24338         
24339          
24340         
24341         var iframe = this.owner.wrap.createChild({
24342             tag: 'iframe',
24343             cls: 'form-control', // bootstrap..
24344             id: this.frameId,
24345             name: this.frameId,
24346             frameBorder : 'no',
24347             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24348         }, this.el
24349         );
24350         
24351         
24352         this.iframe = iframe.dom;
24353
24354          this.assignDocWin();
24355         
24356         this.doc.designMode = 'on';
24357        
24358         this.doc.open();
24359         this.doc.write(this.getDocMarkup());
24360         this.doc.close();
24361
24362         
24363         var task = { // must defer to wait for browser to be ready
24364             run : function(){
24365                 //console.log("run task?" + this.doc.readyState);
24366                 this.assignDocWin();
24367                 if(this.doc.body || this.doc.readyState == 'complete'){
24368                     try {
24369                         this.doc.designMode="on";
24370                     } catch (e) {
24371                         return;
24372                     }
24373                     Roo.TaskMgr.stop(task);
24374                     this.initEditor.defer(10, this);
24375                 }
24376             },
24377             interval : 10,
24378             duration: 10000,
24379             scope: this
24380         };
24381         Roo.TaskMgr.start(task);
24382
24383     },
24384
24385     // private
24386     onResize : function(w, h)
24387     {
24388          Roo.log('resize: ' +w + ',' + h );
24389         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24390         if(!this.iframe){
24391             return;
24392         }
24393         if(typeof w == 'number'){
24394             
24395             this.iframe.style.width = w + 'px';
24396         }
24397         if(typeof h == 'number'){
24398             
24399             this.iframe.style.height = h + 'px';
24400             if(this.doc){
24401                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24402             }
24403         }
24404         
24405     },
24406
24407     /**
24408      * Toggles the editor between standard and source edit mode.
24409      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24410      */
24411     toggleSourceEdit : function(sourceEditMode){
24412         
24413         this.sourceEditMode = sourceEditMode === true;
24414         
24415         if(this.sourceEditMode){
24416  
24417             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24418             
24419         }else{
24420             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24421             //this.iframe.className = '';
24422             this.deferFocus();
24423         }
24424         //this.setSize(this.owner.wrap.getSize());
24425         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24426     },
24427
24428     
24429   
24430
24431     /**
24432      * Protected method that will not generally be called directly. If you need/want
24433      * custom HTML cleanup, this is the method you should override.
24434      * @param {String} html The HTML to be cleaned
24435      * return {String} The cleaned HTML
24436      */
24437     cleanHtml : function(html){
24438         html = String(html);
24439         if(html.length > 5){
24440             if(Roo.isSafari){ // strip safari nonsense
24441                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24442             }
24443         }
24444         if(html == '&nbsp;'){
24445             html = '';
24446         }
24447         return html;
24448     },
24449
24450     /**
24451      * HTML Editor -> Textarea
24452      * Protected method that will not generally be called directly. Syncs the contents
24453      * of the editor iframe with the textarea.
24454      */
24455     syncValue : function(){
24456         if(this.initialized){
24457             var bd = (this.doc.body || this.doc.documentElement);
24458             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24459             var html = bd.innerHTML;
24460             if(Roo.isSafari){
24461                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24462                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24463                 if(m && m[1]){
24464                     html = '<div style="'+m[0]+'">' + html + '</div>';
24465                 }
24466             }
24467             html = this.cleanHtml(html);
24468             // fix up the special chars.. normaly like back quotes in word...
24469             // however we do not want to do this with chinese..
24470             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24471                 
24472                 var cc = match.charCodeAt();
24473
24474                 // Get the character value, handling surrogate pairs
24475                 if (match.length == 2) {
24476                     // It's a surrogate pair, calculate the Unicode code point
24477                     var high = match.charCodeAt(0) - 0xD800;
24478                     var low  = match.charCodeAt(1) - 0xDC00;
24479                     cc = (high * 0x400) + low + 0x10000;
24480                 }  else if (
24481                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24482                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24483                     (cc >= 0xf900 && cc < 0xfb00 )
24484                 ) {
24485                         return match;
24486                 }  
24487          
24488                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24489                 return "&#" + cc + ";";
24490                 
24491                 
24492             });
24493             
24494             
24495              
24496             if(this.owner.fireEvent('beforesync', this, html) !== false){
24497                 this.el.dom.value = html;
24498                 this.owner.fireEvent('sync', this, html);
24499             }
24500         }
24501     },
24502
24503     /**
24504      * Protected method that will not generally be called directly. Pushes the value of the textarea
24505      * into the iframe editor.
24506      */
24507     pushValue : function(){
24508         if(this.initialized){
24509             var v = this.el.dom.value.trim();
24510             
24511 //            if(v.length < 1){
24512 //                v = '&#160;';
24513 //            }
24514             
24515             if(this.owner.fireEvent('beforepush', this, v) !== false){
24516                 var d = (this.doc.body || this.doc.documentElement);
24517                 d.innerHTML = v;
24518                 this.cleanUpPaste();
24519                 this.el.dom.value = d.innerHTML;
24520                 this.owner.fireEvent('push', this, v);
24521             }
24522         }
24523     },
24524
24525     // private
24526     deferFocus : function(){
24527         this.focus.defer(10, this);
24528     },
24529
24530     // doc'ed in Field
24531     focus : function(){
24532         if(this.win && !this.sourceEditMode){
24533             this.win.focus();
24534         }else{
24535             this.el.focus();
24536         }
24537     },
24538     
24539     assignDocWin: function()
24540     {
24541         var iframe = this.iframe;
24542         
24543          if(Roo.isIE){
24544             this.doc = iframe.contentWindow.document;
24545             this.win = iframe.contentWindow;
24546         } else {
24547 //            if (!Roo.get(this.frameId)) {
24548 //                return;
24549 //            }
24550 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24551 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24552             
24553             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24554                 return;
24555             }
24556             
24557             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24558             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24559         }
24560     },
24561     
24562     // private
24563     initEditor : function(){
24564         //console.log("INIT EDITOR");
24565         this.assignDocWin();
24566         
24567         
24568         
24569         this.doc.designMode="on";
24570         this.doc.open();
24571         this.doc.write(this.getDocMarkup());
24572         this.doc.close();
24573         
24574         var dbody = (this.doc.body || this.doc.documentElement);
24575         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24576         // this copies styles from the containing element into thsi one..
24577         // not sure why we need all of this..
24578         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24579         
24580         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24581         //ss['background-attachment'] = 'fixed'; // w3c
24582         dbody.bgProperties = 'fixed'; // ie
24583         //Roo.DomHelper.applyStyles(dbody, ss);
24584         Roo.EventManager.on(this.doc, {
24585             //'mousedown': this.onEditorEvent,
24586             'mouseup': this.onEditorEvent,
24587             'dblclick': this.onEditorEvent,
24588             'click': this.onEditorEvent,
24589             'keyup': this.onEditorEvent,
24590             buffer:100,
24591             scope: this
24592         });
24593         if(Roo.isGecko){
24594             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24595         }
24596         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24597             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24598         }
24599         this.initialized = true;
24600
24601         this.owner.fireEvent('initialize', this);
24602         this.pushValue();
24603     },
24604
24605     // private
24606     onDestroy : function(){
24607         
24608         
24609         
24610         if(this.rendered){
24611             
24612             //for (var i =0; i < this.toolbars.length;i++) {
24613             //    // fixme - ask toolbars for heights?
24614             //    this.toolbars[i].onDestroy();
24615            // }
24616             
24617             //this.wrap.dom.innerHTML = '';
24618             //this.wrap.remove();
24619         }
24620     },
24621
24622     // private
24623     onFirstFocus : function(){
24624         
24625         this.assignDocWin();
24626         
24627         
24628         this.activated = true;
24629          
24630     
24631         if(Roo.isGecko){ // prevent silly gecko errors
24632             this.win.focus();
24633             var s = this.win.getSelection();
24634             if(!s.focusNode || s.focusNode.nodeType != 3){
24635                 var r = s.getRangeAt(0);
24636                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24637                 r.collapse(true);
24638                 this.deferFocus();
24639             }
24640             try{
24641                 this.execCmd('useCSS', true);
24642                 this.execCmd('styleWithCSS', false);
24643             }catch(e){}
24644         }
24645         this.owner.fireEvent('activate', this);
24646     },
24647
24648     // private
24649     adjustFont: function(btn){
24650         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24651         //if(Roo.isSafari){ // safari
24652         //    adjust *= 2;
24653        // }
24654         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24655         if(Roo.isSafari){ // safari
24656             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24657             v =  (v < 10) ? 10 : v;
24658             v =  (v > 48) ? 48 : v;
24659             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24660             
24661         }
24662         
24663         
24664         v = Math.max(1, v+adjust);
24665         
24666         this.execCmd('FontSize', v  );
24667     },
24668
24669     onEditorEvent : function(e)
24670     {
24671         this.owner.fireEvent('editorevent', this, e);
24672       //  this.updateToolbar();
24673         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24674     },
24675
24676     insertTag : function(tg)
24677     {
24678         // could be a bit smarter... -> wrap the current selected tRoo..
24679         if (tg.toLowerCase() == 'span' ||
24680             tg.toLowerCase() == 'code' ||
24681             tg.toLowerCase() == 'sup' ||
24682             tg.toLowerCase() == 'sub' 
24683             ) {
24684             
24685             range = this.createRange(this.getSelection());
24686             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24687             wrappingNode.appendChild(range.extractContents());
24688             range.insertNode(wrappingNode);
24689
24690             return;
24691             
24692             
24693             
24694         }
24695         this.execCmd("formatblock",   tg);
24696         
24697     },
24698     
24699     insertText : function(txt)
24700     {
24701         
24702         
24703         var range = this.createRange();
24704         range.deleteContents();
24705                //alert(Sender.getAttribute('label'));
24706                
24707         range.insertNode(this.doc.createTextNode(txt));
24708     } ,
24709     
24710      
24711
24712     /**
24713      * Executes a Midas editor command on the editor document and performs necessary focus and
24714      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24715      * @param {String} cmd The Midas command
24716      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24717      */
24718     relayCmd : function(cmd, value){
24719         this.win.focus();
24720         this.execCmd(cmd, value);
24721         this.owner.fireEvent('editorevent', this);
24722         //this.updateToolbar();
24723         this.owner.deferFocus();
24724     },
24725
24726     /**
24727      * Executes a Midas editor command directly on the editor document.
24728      * For visual commands, you should use {@link #relayCmd} instead.
24729      * <b>This should only be called after the editor is initialized.</b>
24730      * @param {String} cmd The Midas command
24731      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24732      */
24733     execCmd : function(cmd, value){
24734         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24735         this.syncValue();
24736     },
24737  
24738  
24739    
24740     /**
24741      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24742      * to insert tRoo.
24743      * @param {String} text | dom node.. 
24744      */
24745     insertAtCursor : function(text)
24746     {
24747         
24748         if(!this.activated){
24749             return;
24750         }
24751         /*
24752         if(Roo.isIE){
24753             this.win.focus();
24754             var r = this.doc.selection.createRange();
24755             if(r){
24756                 r.collapse(true);
24757                 r.pasteHTML(text);
24758                 this.syncValue();
24759                 this.deferFocus();
24760             
24761             }
24762             return;
24763         }
24764         */
24765         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24766             this.win.focus();
24767             
24768             
24769             // from jquery ui (MIT licenced)
24770             var range, node;
24771             var win = this.win;
24772             
24773             if (win.getSelection && win.getSelection().getRangeAt) {
24774                 range = win.getSelection().getRangeAt(0);
24775                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24776                 range.insertNode(node);
24777             } else if (win.document.selection && win.document.selection.createRange) {
24778                 // no firefox support
24779                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24780                 win.document.selection.createRange().pasteHTML(txt);
24781             } else {
24782                 // no firefox support
24783                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24784                 this.execCmd('InsertHTML', txt);
24785             } 
24786             
24787             this.syncValue();
24788             
24789             this.deferFocus();
24790         }
24791     },
24792  // private
24793     mozKeyPress : function(e){
24794         if(e.ctrlKey){
24795             var c = e.getCharCode(), cmd;
24796           
24797             if(c > 0){
24798                 c = String.fromCharCode(c).toLowerCase();
24799                 switch(c){
24800                     case 'b':
24801                         cmd = 'bold';
24802                         break;
24803                     case 'i':
24804                         cmd = 'italic';
24805                         break;
24806                     
24807                     case 'u':
24808                         cmd = 'underline';
24809                         break;
24810                     
24811                     case 'v':
24812                         this.cleanUpPaste.defer(100, this);
24813                         return;
24814                         
24815                 }
24816                 if(cmd){
24817                     this.win.focus();
24818                     this.execCmd(cmd);
24819                     this.deferFocus();
24820                     e.preventDefault();
24821                 }
24822                 
24823             }
24824         }
24825     },
24826
24827     // private
24828     fixKeys : function(){ // load time branching for fastest keydown performance
24829         if(Roo.isIE){
24830             return function(e){
24831                 var k = e.getKey(), r;
24832                 if(k == e.TAB){
24833                     e.stopEvent();
24834                     r = this.doc.selection.createRange();
24835                     if(r){
24836                         r.collapse(true);
24837                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24838                         this.deferFocus();
24839                     }
24840                     return;
24841                 }
24842                 
24843                 if(k == e.ENTER){
24844                     r = this.doc.selection.createRange();
24845                     if(r){
24846                         var target = r.parentElement();
24847                         if(!target || target.tagName.toLowerCase() != 'li'){
24848                             e.stopEvent();
24849                             r.pasteHTML('<br />');
24850                             r.collapse(false);
24851                             r.select();
24852                         }
24853                     }
24854                 }
24855                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24856                     this.cleanUpPaste.defer(100, this);
24857                     return;
24858                 }
24859                 
24860                 
24861             };
24862         }else if(Roo.isOpera){
24863             return function(e){
24864                 var k = e.getKey();
24865                 if(k == e.TAB){
24866                     e.stopEvent();
24867                     this.win.focus();
24868                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24869                     this.deferFocus();
24870                 }
24871                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24872                     this.cleanUpPaste.defer(100, this);
24873                     return;
24874                 }
24875                 
24876             };
24877         }else if(Roo.isSafari){
24878             return function(e){
24879                 var k = e.getKey();
24880                 
24881                 if(k == e.TAB){
24882                     e.stopEvent();
24883                     this.execCmd('InsertText','\t');
24884                     this.deferFocus();
24885                     return;
24886                 }
24887                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24888                     this.cleanUpPaste.defer(100, this);
24889                     return;
24890                 }
24891                 
24892              };
24893         }
24894     }(),
24895     
24896     getAllAncestors: function()
24897     {
24898         var p = this.getSelectedNode();
24899         var a = [];
24900         if (!p) {
24901             a.push(p); // push blank onto stack..
24902             p = this.getParentElement();
24903         }
24904         
24905         
24906         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24907             a.push(p);
24908             p = p.parentNode;
24909         }
24910         a.push(this.doc.body);
24911         return a;
24912     },
24913     lastSel : false,
24914     lastSelNode : false,
24915     
24916     
24917     getSelection : function() 
24918     {
24919         this.assignDocWin();
24920         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24921     },
24922     
24923     getSelectedNode: function() 
24924     {
24925         // this may only work on Gecko!!!
24926         
24927         // should we cache this!!!!
24928         
24929         
24930         
24931          
24932         var range = this.createRange(this.getSelection()).cloneRange();
24933         
24934         if (Roo.isIE) {
24935             var parent = range.parentElement();
24936             while (true) {
24937                 var testRange = range.duplicate();
24938                 testRange.moveToElementText(parent);
24939                 if (testRange.inRange(range)) {
24940                     break;
24941                 }
24942                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24943                     break;
24944                 }
24945                 parent = parent.parentElement;
24946             }
24947             return parent;
24948         }
24949         
24950         // is ancestor a text element.
24951         var ac =  range.commonAncestorContainer;
24952         if (ac.nodeType == 3) {
24953             ac = ac.parentNode;
24954         }
24955         
24956         var ar = ac.childNodes;
24957          
24958         var nodes = [];
24959         var other_nodes = [];
24960         var has_other_nodes = false;
24961         for (var i=0;i<ar.length;i++) {
24962             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24963                 continue;
24964             }
24965             // fullly contained node.
24966             
24967             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24968                 nodes.push(ar[i]);
24969                 continue;
24970             }
24971             
24972             // probably selected..
24973             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24974                 other_nodes.push(ar[i]);
24975                 continue;
24976             }
24977             // outer..
24978             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24979                 continue;
24980             }
24981             
24982             
24983             has_other_nodes = true;
24984         }
24985         if (!nodes.length && other_nodes.length) {
24986             nodes= other_nodes;
24987         }
24988         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24989             return false;
24990         }
24991         
24992         return nodes[0];
24993     },
24994     createRange: function(sel)
24995     {
24996         // this has strange effects when using with 
24997         // top toolbar - not sure if it's a great idea.
24998         //this.editor.contentWindow.focus();
24999         if (typeof sel != "undefined") {
25000             try {
25001                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25002             } catch(e) {
25003                 return this.doc.createRange();
25004             }
25005         } else {
25006             return this.doc.createRange();
25007         }
25008     },
25009     getParentElement: function()
25010     {
25011         
25012         this.assignDocWin();
25013         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25014         
25015         var range = this.createRange(sel);
25016          
25017         try {
25018             var p = range.commonAncestorContainer;
25019             while (p.nodeType == 3) { // text node
25020                 p = p.parentNode;
25021             }
25022             return p;
25023         } catch (e) {
25024             return null;
25025         }
25026     
25027     },
25028     /***
25029      *
25030      * Range intersection.. the hard stuff...
25031      *  '-1' = before
25032      *  '0' = hits..
25033      *  '1' = after.
25034      *         [ -- selected range --- ]
25035      *   [fail]                        [fail]
25036      *
25037      *    basically..
25038      *      if end is before start or  hits it. fail.
25039      *      if start is after end or hits it fail.
25040      *
25041      *   if either hits (but other is outside. - then it's not 
25042      *   
25043      *    
25044      **/
25045     
25046     
25047     // @see http://www.thismuchiknow.co.uk/?p=64.
25048     rangeIntersectsNode : function(range, node)
25049     {
25050         var nodeRange = node.ownerDocument.createRange();
25051         try {
25052             nodeRange.selectNode(node);
25053         } catch (e) {
25054             nodeRange.selectNodeContents(node);
25055         }
25056     
25057         var rangeStartRange = range.cloneRange();
25058         rangeStartRange.collapse(true);
25059     
25060         var rangeEndRange = range.cloneRange();
25061         rangeEndRange.collapse(false);
25062     
25063         var nodeStartRange = nodeRange.cloneRange();
25064         nodeStartRange.collapse(true);
25065     
25066         var nodeEndRange = nodeRange.cloneRange();
25067         nodeEndRange.collapse(false);
25068     
25069         return rangeStartRange.compareBoundaryPoints(
25070                  Range.START_TO_START, nodeEndRange) == -1 &&
25071                rangeEndRange.compareBoundaryPoints(
25072                  Range.START_TO_START, nodeStartRange) == 1;
25073         
25074          
25075     },
25076     rangeCompareNode : function(range, node)
25077     {
25078         var nodeRange = node.ownerDocument.createRange();
25079         try {
25080             nodeRange.selectNode(node);
25081         } catch (e) {
25082             nodeRange.selectNodeContents(node);
25083         }
25084         
25085         
25086         range.collapse(true);
25087     
25088         nodeRange.collapse(true);
25089      
25090         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25091         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25092          
25093         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25094         
25095         var nodeIsBefore   =  ss == 1;
25096         var nodeIsAfter    = ee == -1;
25097         
25098         if (nodeIsBefore && nodeIsAfter) {
25099             return 0; // outer
25100         }
25101         if (!nodeIsBefore && nodeIsAfter) {
25102             return 1; //right trailed.
25103         }
25104         
25105         if (nodeIsBefore && !nodeIsAfter) {
25106             return 2;  // left trailed.
25107         }
25108         // fully contined.
25109         return 3;
25110     },
25111
25112     // private? - in a new class?
25113     cleanUpPaste :  function()
25114     {
25115         // cleans up the whole document..
25116         Roo.log('cleanuppaste');
25117         
25118         this.cleanUpChildren(this.doc.body);
25119         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25120         if (clean != this.doc.body.innerHTML) {
25121             this.doc.body.innerHTML = clean;
25122         }
25123         
25124     },
25125     
25126     cleanWordChars : function(input) {// change the chars to hex code
25127         var he = Roo.HtmlEditorCore;
25128         
25129         var output = input;
25130         Roo.each(he.swapCodes, function(sw) { 
25131             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25132             
25133             output = output.replace(swapper, sw[1]);
25134         });
25135         
25136         return output;
25137     },
25138     
25139     
25140     cleanUpChildren : function (n)
25141     {
25142         if (!n.childNodes.length) {
25143             return;
25144         }
25145         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25146            this.cleanUpChild(n.childNodes[i]);
25147         }
25148     },
25149     
25150     
25151         
25152     
25153     cleanUpChild : function (node)
25154     {
25155         var ed = this;
25156         //console.log(node);
25157         if (node.nodeName == "#text") {
25158             // clean up silly Windows -- stuff?
25159             return; 
25160         }
25161         if (node.nodeName == "#comment") {
25162             node.parentNode.removeChild(node);
25163             // clean up silly Windows -- stuff?
25164             return; 
25165         }
25166         var lcname = node.tagName.toLowerCase();
25167         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25168         // whitelist of tags..
25169         
25170         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25171             // remove node.
25172             node.parentNode.removeChild(node);
25173             return;
25174             
25175         }
25176         
25177         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25178         
25179         // spans with no attributes - just remove them..
25180         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25181             remove_keep_children = true;
25182         }
25183         
25184         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25185         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25186         
25187         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25188         //    remove_keep_children = true;
25189         //}
25190         
25191         if (remove_keep_children) {
25192             this.cleanUpChildren(node);
25193             // inserts everything just before this node...
25194             while (node.childNodes.length) {
25195                 var cn = node.childNodes[0];
25196                 node.removeChild(cn);
25197                 node.parentNode.insertBefore(cn, node);
25198             }
25199             node.parentNode.removeChild(node);
25200             return;
25201         }
25202         
25203         if (!node.attributes || !node.attributes.length) {
25204             
25205           
25206             
25207             
25208             this.cleanUpChildren(node);
25209             return;
25210         }
25211         
25212         function cleanAttr(n,v)
25213         {
25214             
25215             if (v.match(/^\./) || v.match(/^\//)) {
25216                 return;
25217             }
25218             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25219                 return;
25220             }
25221             if (v.match(/^#/)) {
25222                 return;
25223             }
25224             if (v.match(/^\{/)) { // allow template editing.
25225                 return;
25226             }
25227 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25228             node.removeAttribute(n);
25229             
25230         }
25231         
25232         var cwhite = this.cwhite;
25233         var cblack = this.cblack;
25234             
25235         function cleanStyle(n,v)
25236         {
25237             if (v.match(/expression/)) { //XSS?? should we even bother..
25238                 node.removeAttribute(n);
25239                 return;
25240             }
25241             
25242             var parts = v.split(/;/);
25243             var clean = [];
25244             
25245             Roo.each(parts, function(p) {
25246                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25247                 if (!p.length) {
25248                     return true;
25249                 }
25250                 var l = p.split(':').shift().replace(/\s+/g,'');
25251                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25252                 
25253                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25254 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25255                     //node.removeAttribute(n);
25256                     return true;
25257                 }
25258                 //Roo.log()
25259                 // only allow 'c whitelisted system attributes'
25260                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25261 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25262                     //node.removeAttribute(n);
25263                     return true;
25264                 }
25265                 
25266                 
25267                  
25268                 
25269                 clean.push(p);
25270                 return true;
25271             });
25272             if (clean.length) { 
25273                 node.setAttribute(n, clean.join(';'));
25274             } else {
25275                 node.removeAttribute(n);
25276             }
25277             
25278         }
25279         
25280         
25281         for (var i = node.attributes.length-1; i > -1 ; i--) {
25282             var a = node.attributes[i];
25283             //console.log(a);
25284             
25285             if (a.name.toLowerCase().substr(0,2)=='on')  {
25286                 node.removeAttribute(a.name);
25287                 continue;
25288             }
25289             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25290                 node.removeAttribute(a.name);
25291                 continue;
25292             }
25293             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25294                 cleanAttr(a.name,a.value); // fixme..
25295                 continue;
25296             }
25297             if (a.name == 'style') {
25298                 cleanStyle(a.name,a.value);
25299                 continue;
25300             }
25301             /// clean up MS crap..
25302             // tecnically this should be a list of valid class'es..
25303             
25304             
25305             if (a.name == 'class') {
25306                 if (a.value.match(/^Mso/)) {
25307                     node.removeAttribute('class');
25308                 }
25309                 
25310                 if (a.value.match(/^body$/)) {
25311                     node.removeAttribute('class');
25312                 }
25313                 continue;
25314             }
25315             
25316             // style cleanup!?
25317             // class cleanup?
25318             
25319         }
25320         
25321         
25322         this.cleanUpChildren(node);
25323         
25324         
25325     },
25326     
25327     /**
25328      * Clean up MS wordisms...
25329      */
25330     cleanWord : function(node)
25331     {
25332         if (!node) {
25333             this.cleanWord(this.doc.body);
25334             return;
25335         }
25336         
25337         if(
25338                 node.nodeName == 'SPAN' &&
25339                 !node.hasAttributes() &&
25340                 node.childNodes.length == 1 &&
25341                 node.firstChild.nodeName == "#text"  
25342         ) {
25343             var textNode = node.firstChild;
25344             node.removeChild(textNode);
25345             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25346                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25347             }
25348             node.parentNode.insertBefore(textNode, node);
25349             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25350                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25351             }
25352             node.parentNode.removeChild(node);
25353         }
25354         
25355         if (node.nodeName == "#text") {
25356             // clean up silly Windows -- stuff?
25357             return; 
25358         }
25359         if (node.nodeName == "#comment") {
25360             node.parentNode.removeChild(node);
25361             // clean up silly Windows -- stuff?
25362             return; 
25363         }
25364         
25365         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25366             node.parentNode.removeChild(node);
25367             return;
25368         }
25369         //Roo.log(node.tagName);
25370         // remove - but keep children..
25371         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25372             //Roo.log('-- removed');
25373             while (node.childNodes.length) {
25374                 var cn = node.childNodes[0];
25375                 node.removeChild(cn);
25376                 node.parentNode.insertBefore(cn, node);
25377                 // move node to parent - and clean it..
25378                 this.cleanWord(cn);
25379             }
25380             node.parentNode.removeChild(node);
25381             /// no need to iterate chidlren = it's got none..
25382             //this.iterateChildren(node, this.cleanWord);
25383             return;
25384         }
25385         // clean styles
25386         if (node.className.length) {
25387             
25388             var cn = node.className.split(/\W+/);
25389             var cna = [];
25390             Roo.each(cn, function(cls) {
25391                 if (cls.match(/Mso[a-zA-Z]+/)) {
25392                     return;
25393                 }
25394                 cna.push(cls);
25395             });
25396             node.className = cna.length ? cna.join(' ') : '';
25397             if (!cna.length) {
25398                 node.removeAttribute("class");
25399             }
25400         }
25401         
25402         if (node.hasAttribute("lang")) {
25403             node.removeAttribute("lang");
25404         }
25405         
25406         if (node.hasAttribute("style")) {
25407             
25408             var styles = node.getAttribute("style").split(";");
25409             var nstyle = [];
25410             Roo.each(styles, function(s) {
25411                 if (!s.match(/:/)) {
25412                     return;
25413                 }
25414                 var kv = s.split(":");
25415                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25416                     return;
25417                 }
25418                 // what ever is left... we allow.
25419                 nstyle.push(s);
25420             });
25421             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25422             if (!nstyle.length) {
25423                 node.removeAttribute('style');
25424             }
25425         }
25426         this.iterateChildren(node, this.cleanWord);
25427         
25428         
25429         
25430     },
25431     /**
25432      * iterateChildren of a Node, calling fn each time, using this as the scole..
25433      * @param {DomNode} node node to iterate children of.
25434      * @param {Function} fn method of this class to call on each item.
25435      */
25436     iterateChildren : function(node, fn)
25437     {
25438         if (!node.childNodes.length) {
25439                 return;
25440         }
25441         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25442            fn.call(this, node.childNodes[i])
25443         }
25444     },
25445     
25446     
25447     /**
25448      * cleanTableWidths.
25449      *
25450      * Quite often pasting from word etc.. results in tables with column and widths.
25451      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25452      *
25453      */
25454     cleanTableWidths : function(node)
25455     {
25456          
25457          
25458         if (!node) {
25459             this.cleanTableWidths(this.doc.body);
25460             return;
25461         }
25462         
25463         // ignore list...
25464         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25465             return; 
25466         }
25467         Roo.log(node.tagName);
25468         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25469             this.iterateChildren(node, this.cleanTableWidths);
25470             return;
25471         }
25472         if (node.hasAttribute('width')) {
25473             node.removeAttribute('width');
25474         }
25475         
25476          
25477         if (node.hasAttribute("style")) {
25478             // pretty basic...
25479             
25480             var styles = node.getAttribute("style").split(";");
25481             var nstyle = [];
25482             Roo.each(styles, function(s) {
25483                 if (!s.match(/:/)) {
25484                     return;
25485                 }
25486                 var kv = s.split(":");
25487                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25488                     return;
25489                 }
25490                 // what ever is left... we allow.
25491                 nstyle.push(s);
25492             });
25493             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25494             if (!nstyle.length) {
25495                 node.removeAttribute('style');
25496             }
25497         }
25498         
25499         this.iterateChildren(node, this.cleanTableWidths);
25500         
25501         
25502     },
25503     
25504     
25505     
25506     
25507     domToHTML : function(currentElement, depth, nopadtext) {
25508         
25509         depth = depth || 0;
25510         nopadtext = nopadtext || false;
25511     
25512         if (!currentElement) {
25513             return this.domToHTML(this.doc.body);
25514         }
25515         
25516         //Roo.log(currentElement);
25517         var j;
25518         var allText = false;
25519         var nodeName = currentElement.nodeName;
25520         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25521         
25522         if  (nodeName == '#text') {
25523             
25524             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25525         }
25526         
25527         
25528         var ret = '';
25529         if (nodeName != 'BODY') {
25530              
25531             var i = 0;
25532             // Prints the node tagName, such as <A>, <IMG>, etc
25533             if (tagName) {
25534                 var attr = [];
25535                 for(i = 0; i < currentElement.attributes.length;i++) {
25536                     // quoting?
25537                     var aname = currentElement.attributes.item(i).name;
25538                     if (!currentElement.attributes.item(i).value.length) {
25539                         continue;
25540                     }
25541                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25542                 }
25543                 
25544                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25545             } 
25546             else {
25547                 
25548                 // eack
25549             }
25550         } else {
25551             tagName = false;
25552         }
25553         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25554             return ret;
25555         }
25556         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25557             nopadtext = true;
25558         }
25559         
25560         
25561         // Traverse the tree
25562         i = 0;
25563         var currentElementChild = currentElement.childNodes.item(i);
25564         var allText = true;
25565         var innerHTML  = '';
25566         lastnode = '';
25567         while (currentElementChild) {
25568             // Formatting code (indent the tree so it looks nice on the screen)
25569             var nopad = nopadtext;
25570             if (lastnode == 'SPAN') {
25571                 nopad  = true;
25572             }
25573             // text
25574             if  (currentElementChild.nodeName == '#text') {
25575                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25576                 toadd = nopadtext ? toadd : toadd.trim();
25577                 if (!nopad && toadd.length > 80) {
25578                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25579                 }
25580                 innerHTML  += toadd;
25581                 
25582                 i++;
25583                 currentElementChild = currentElement.childNodes.item(i);
25584                 lastNode = '';
25585                 continue;
25586             }
25587             allText = false;
25588             
25589             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25590                 
25591             // Recursively traverse the tree structure of the child node
25592             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25593             lastnode = currentElementChild.nodeName;
25594             i++;
25595             currentElementChild=currentElement.childNodes.item(i);
25596         }
25597         
25598         ret += innerHTML;
25599         
25600         if (!allText) {
25601                 // The remaining code is mostly for formatting the tree
25602             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25603         }
25604         
25605         
25606         if (tagName) {
25607             ret+= "</"+tagName+">";
25608         }
25609         return ret;
25610         
25611     },
25612         
25613     applyBlacklists : function()
25614     {
25615         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25616         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25617         
25618         this.white = [];
25619         this.black = [];
25620         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25621             if (b.indexOf(tag) > -1) {
25622                 return;
25623             }
25624             this.white.push(tag);
25625             
25626         }, this);
25627         
25628         Roo.each(w, function(tag) {
25629             if (b.indexOf(tag) > -1) {
25630                 return;
25631             }
25632             if (this.white.indexOf(tag) > -1) {
25633                 return;
25634             }
25635             this.white.push(tag);
25636             
25637         }, this);
25638         
25639         
25640         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25641             if (w.indexOf(tag) > -1) {
25642                 return;
25643             }
25644             this.black.push(tag);
25645             
25646         }, this);
25647         
25648         Roo.each(b, function(tag) {
25649             if (w.indexOf(tag) > -1) {
25650                 return;
25651             }
25652             if (this.black.indexOf(tag) > -1) {
25653                 return;
25654             }
25655             this.black.push(tag);
25656             
25657         }, this);
25658         
25659         
25660         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25661         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25662         
25663         this.cwhite = [];
25664         this.cblack = [];
25665         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25666             if (b.indexOf(tag) > -1) {
25667                 return;
25668             }
25669             this.cwhite.push(tag);
25670             
25671         }, this);
25672         
25673         Roo.each(w, function(tag) {
25674             if (b.indexOf(tag) > -1) {
25675                 return;
25676             }
25677             if (this.cwhite.indexOf(tag) > -1) {
25678                 return;
25679             }
25680             this.cwhite.push(tag);
25681             
25682         }, this);
25683         
25684         
25685         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25686             if (w.indexOf(tag) > -1) {
25687                 return;
25688             }
25689             this.cblack.push(tag);
25690             
25691         }, this);
25692         
25693         Roo.each(b, function(tag) {
25694             if (w.indexOf(tag) > -1) {
25695                 return;
25696             }
25697             if (this.cblack.indexOf(tag) > -1) {
25698                 return;
25699             }
25700             this.cblack.push(tag);
25701             
25702         }, this);
25703     },
25704     
25705     setStylesheets : function(stylesheets)
25706     {
25707         if(typeof(stylesheets) == 'string'){
25708             Roo.get(this.iframe.contentDocument.head).createChild({
25709                 tag : 'link',
25710                 rel : 'stylesheet',
25711                 type : 'text/css',
25712                 href : stylesheets
25713             });
25714             
25715             return;
25716         }
25717         var _this = this;
25718      
25719         Roo.each(stylesheets, function(s) {
25720             if(!s.length){
25721                 return;
25722             }
25723             
25724             Roo.get(_this.iframe.contentDocument.head).createChild({
25725                 tag : 'link',
25726                 rel : 'stylesheet',
25727                 type : 'text/css',
25728                 href : s
25729             });
25730         });
25731
25732         
25733     },
25734     
25735     removeStylesheets : function()
25736     {
25737         var _this = this;
25738         
25739         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25740             s.remove();
25741         });
25742     },
25743     
25744     setStyle : function(style)
25745     {
25746         Roo.get(this.iframe.contentDocument.head).createChild({
25747             tag : 'style',
25748             type : 'text/css',
25749             html : style
25750         });
25751
25752         return;
25753     }
25754     
25755     // hide stuff that is not compatible
25756     /**
25757      * @event blur
25758      * @hide
25759      */
25760     /**
25761      * @event change
25762      * @hide
25763      */
25764     /**
25765      * @event focus
25766      * @hide
25767      */
25768     /**
25769      * @event specialkey
25770      * @hide
25771      */
25772     /**
25773      * @cfg {String} fieldClass @hide
25774      */
25775     /**
25776      * @cfg {String} focusClass @hide
25777      */
25778     /**
25779      * @cfg {String} autoCreate @hide
25780      */
25781     /**
25782      * @cfg {String} inputType @hide
25783      */
25784     /**
25785      * @cfg {String} invalidClass @hide
25786      */
25787     /**
25788      * @cfg {String} invalidText @hide
25789      */
25790     /**
25791      * @cfg {String} msgFx @hide
25792      */
25793     /**
25794      * @cfg {String} validateOnBlur @hide
25795      */
25796 });
25797
25798 Roo.HtmlEditorCore.white = [
25799         'area', 'br', 'img', 'input', 'hr', 'wbr',
25800         
25801        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25802        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25803        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25804        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25805        'table',   'ul',         'xmp', 
25806        
25807        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25808       'thead',   'tr', 
25809      
25810       'dir', 'menu', 'ol', 'ul', 'dl',
25811        
25812       'embed',  'object'
25813 ];
25814
25815
25816 Roo.HtmlEditorCore.black = [
25817     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25818         'applet', // 
25819         'base',   'basefont', 'bgsound', 'blink',  'body', 
25820         'frame',  'frameset', 'head',    'html',   'ilayer', 
25821         'iframe', 'layer',  'link',     'meta',    'object',   
25822         'script', 'style' ,'title',  'xml' // clean later..
25823 ];
25824 Roo.HtmlEditorCore.clean = [
25825     'script', 'style', 'title', 'xml'
25826 ];
25827 Roo.HtmlEditorCore.remove = [
25828     'font'
25829 ];
25830 // attributes..
25831
25832 Roo.HtmlEditorCore.ablack = [
25833     'on'
25834 ];
25835     
25836 Roo.HtmlEditorCore.aclean = [ 
25837     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25838 ];
25839
25840 // protocols..
25841 Roo.HtmlEditorCore.pwhite= [
25842         'http',  'https',  'mailto'
25843 ];
25844
25845 // white listed style attributes.
25846 Roo.HtmlEditorCore.cwhite= [
25847       //  'text-align', /// default is to allow most things..
25848       
25849          
25850 //        'font-size'//??
25851 ];
25852
25853 // black listed style attributes.
25854 Roo.HtmlEditorCore.cblack= [
25855       //  'font-size' -- this can be set by the project 
25856 ];
25857
25858
25859 Roo.HtmlEditorCore.swapCodes   =[ 
25860     [    8211, "&#8211;" ], 
25861     [    8212, "&#8212;" ], 
25862     [    8216,  "'" ],  
25863     [    8217, "'" ],  
25864     [    8220, '"' ],  
25865     [    8221, '"' ],  
25866     [    8226, "*" ],  
25867     [    8230, "..." ]
25868 ]; 
25869
25870     /*
25871  * - LGPL
25872  *
25873  * HtmlEditor
25874  * 
25875  */
25876
25877 /**
25878  * @class Roo.bootstrap.HtmlEditor
25879  * @extends Roo.bootstrap.TextArea
25880  * Bootstrap HtmlEditor class
25881
25882  * @constructor
25883  * Create a new HtmlEditor
25884  * @param {Object} config The config object
25885  */
25886
25887 Roo.bootstrap.HtmlEditor = function(config){
25888     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25889     if (!this.toolbars) {
25890         this.toolbars = [];
25891     }
25892     
25893     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25894     this.addEvents({
25895             /**
25896              * @event initialize
25897              * Fires when the editor is fully initialized (including the iframe)
25898              * @param {HtmlEditor} this
25899              */
25900             initialize: true,
25901             /**
25902              * @event activate
25903              * Fires when the editor is first receives the focus. Any insertion must wait
25904              * until after this event.
25905              * @param {HtmlEditor} this
25906              */
25907             activate: true,
25908              /**
25909              * @event beforesync
25910              * Fires before the textarea is updated with content from the editor iframe. Return false
25911              * to cancel the sync.
25912              * @param {HtmlEditor} this
25913              * @param {String} html
25914              */
25915             beforesync: true,
25916              /**
25917              * @event beforepush
25918              * Fires before the iframe editor is updated with content from the textarea. Return false
25919              * to cancel the push.
25920              * @param {HtmlEditor} this
25921              * @param {String} html
25922              */
25923             beforepush: true,
25924              /**
25925              * @event sync
25926              * Fires when the textarea is updated with content from the editor iframe.
25927              * @param {HtmlEditor} this
25928              * @param {String} html
25929              */
25930             sync: true,
25931              /**
25932              * @event push
25933              * Fires when the iframe editor is updated with content from the textarea.
25934              * @param {HtmlEditor} this
25935              * @param {String} html
25936              */
25937             push: true,
25938              /**
25939              * @event editmodechange
25940              * Fires when the editor switches edit modes
25941              * @param {HtmlEditor} this
25942              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25943              */
25944             editmodechange: true,
25945             /**
25946              * @event editorevent
25947              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25948              * @param {HtmlEditor} this
25949              */
25950             editorevent: true,
25951             /**
25952              * @event firstfocus
25953              * Fires when on first focus - needed by toolbars..
25954              * @param {HtmlEditor} this
25955              */
25956             firstfocus: true,
25957             /**
25958              * @event autosave
25959              * Auto save the htmlEditor value as a file into Events
25960              * @param {HtmlEditor} this
25961              */
25962             autosave: true,
25963             /**
25964              * @event savedpreview
25965              * preview the saved version of htmlEditor
25966              * @param {HtmlEditor} this
25967              */
25968             savedpreview: true
25969         });
25970 };
25971
25972
25973 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25974     
25975     
25976       /**
25977      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25978      */
25979     toolbars : false,
25980     
25981      /**
25982     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25983     */
25984     btns : [],
25985    
25986      /**
25987      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25988      *                        Roo.resizable.
25989      */
25990     resizable : false,
25991      /**
25992      * @cfg {Number} height (in pixels)
25993      */   
25994     height: 300,
25995    /**
25996      * @cfg {Number} width (in pixels)
25997      */   
25998     width: false,
25999     
26000     /**
26001      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26002      * 
26003      */
26004     stylesheets: false,
26005     
26006     // id of frame..
26007     frameId: false,
26008     
26009     // private properties
26010     validationEvent : false,
26011     deferHeight: true,
26012     initialized : false,
26013     activated : false,
26014     
26015     onFocus : Roo.emptyFn,
26016     iframePad:3,
26017     hideMode:'offsets',
26018     
26019     tbContainer : false,
26020     
26021     bodyCls : '',
26022     
26023     toolbarContainer :function() {
26024         return this.wrap.select('.x-html-editor-tb',true).first();
26025     },
26026
26027     /**
26028      * Protected method that will not generally be called directly. It
26029      * is called when the editor creates its toolbar. Override this method if you need to
26030      * add custom toolbar buttons.
26031      * @param {HtmlEditor} editor
26032      */
26033     createToolbar : function(){
26034         Roo.log('renewing');
26035         Roo.log("create toolbars");
26036         
26037         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26038         this.toolbars[0].render(this.toolbarContainer());
26039         
26040         return;
26041         
26042 //        if (!editor.toolbars || !editor.toolbars.length) {
26043 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26044 //        }
26045 //        
26046 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26047 //            editor.toolbars[i] = Roo.factory(
26048 //                    typeof(editor.toolbars[i]) == 'string' ?
26049 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26050 //                Roo.bootstrap.HtmlEditor);
26051 //            editor.toolbars[i].init(editor);
26052 //        }
26053     },
26054
26055      
26056     // private
26057     onRender : function(ct, position)
26058     {
26059        // Roo.log("Call onRender: " + this.xtype);
26060         var _t = this;
26061         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26062       
26063         this.wrap = this.inputEl().wrap({
26064             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26065         });
26066         
26067         this.editorcore.onRender(ct, position);
26068          
26069         if (this.resizable) {
26070             this.resizeEl = new Roo.Resizable(this.wrap, {
26071                 pinned : true,
26072                 wrap: true,
26073                 dynamic : true,
26074                 minHeight : this.height,
26075                 height: this.height,
26076                 handles : this.resizable,
26077                 width: this.width,
26078                 listeners : {
26079                     resize : function(r, w, h) {
26080                         _t.onResize(w,h); // -something
26081                     }
26082                 }
26083             });
26084             
26085         }
26086         this.createToolbar(this);
26087        
26088         
26089         if(!this.width && this.resizable){
26090             this.setSize(this.wrap.getSize());
26091         }
26092         if (this.resizeEl) {
26093             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26094             // should trigger onReize..
26095         }
26096         
26097     },
26098
26099     // private
26100     onResize : function(w, h)
26101     {
26102         Roo.log('resize: ' +w + ',' + h );
26103         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26104         var ew = false;
26105         var eh = false;
26106         
26107         if(this.inputEl() ){
26108             if(typeof w == 'number'){
26109                 var aw = w - this.wrap.getFrameWidth('lr');
26110                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26111                 ew = aw;
26112             }
26113             if(typeof h == 'number'){
26114                  var tbh = -11;  // fixme it needs to tool bar size!
26115                 for (var i =0; i < this.toolbars.length;i++) {
26116                     // fixme - ask toolbars for heights?
26117                     tbh += this.toolbars[i].el.getHeight();
26118                     //if (this.toolbars[i].footer) {
26119                     //    tbh += this.toolbars[i].footer.el.getHeight();
26120                     //}
26121                 }
26122               
26123                 
26124                 
26125                 
26126                 
26127                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26128                 ah -= 5; // knock a few pixes off for look..
26129                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26130                 var eh = ah;
26131             }
26132         }
26133         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26134         this.editorcore.onResize(ew,eh);
26135         
26136     },
26137
26138     /**
26139      * Toggles the editor between standard and source edit mode.
26140      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26141      */
26142     toggleSourceEdit : function(sourceEditMode)
26143     {
26144         this.editorcore.toggleSourceEdit(sourceEditMode);
26145         
26146         if(this.editorcore.sourceEditMode){
26147             Roo.log('editor - showing textarea');
26148             
26149 //            Roo.log('in');
26150 //            Roo.log(this.syncValue());
26151             this.syncValue();
26152             this.inputEl().removeClass(['hide', 'x-hidden']);
26153             this.inputEl().dom.removeAttribute('tabIndex');
26154             this.inputEl().focus();
26155         }else{
26156             Roo.log('editor - hiding textarea');
26157 //            Roo.log('out')
26158 //            Roo.log(this.pushValue()); 
26159             this.pushValue();
26160             
26161             this.inputEl().addClass(['hide', 'x-hidden']);
26162             this.inputEl().dom.setAttribute('tabIndex', -1);
26163             //this.deferFocus();
26164         }
26165          
26166         if(this.resizable){
26167             this.setSize(this.wrap.getSize());
26168         }
26169         
26170         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26171     },
26172  
26173     // private (for BoxComponent)
26174     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26175
26176     // private (for BoxComponent)
26177     getResizeEl : function(){
26178         return this.wrap;
26179     },
26180
26181     // private (for BoxComponent)
26182     getPositionEl : function(){
26183         return this.wrap;
26184     },
26185
26186     // private
26187     initEvents : function(){
26188         this.originalValue = this.getValue();
26189     },
26190
26191 //    /**
26192 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26193 //     * @method
26194 //     */
26195 //    markInvalid : Roo.emptyFn,
26196 //    /**
26197 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26198 //     * @method
26199 //     */
26200 //    clearInvalid : Roo.emptyFn,
26201
26202     setValue : function(v){
26203         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26204         this.editorcore.pushValue();
26205     },
26206
26207      
26208     // private
26209     deferFocus : function(){
26210         this.focus.defer(10, this);
26211     },
26212
26213     // doc'ed in Field
26214     focus : function(){
26215         this.editorcore.focus();
26216         
26217     },
26218       
26219
26220     // private
26221     onDestroy : function(){
26222         
26223         
26224         
26225         if(this.rendered){
26226             
26227             for (var i =0; i < this.toolbars.length;i++) {
26228                 // fixme - ask toolbars for heights?
26229                 this.toolbars[i].onDestroy();
26230             }
26231             
26232             this.wrap.dom.innerHTML = '';
26233             this.wrap.remove();
26234         }
26235     },
26236
26237     // private
26238     onFirstFocus : function(){
26239         //Roo.log("onFirstFocus");
26240         this.editorcore.onFirstFocus();
26241          for (var i =0; i < this.toolbars.length;i++) {
26242             this.toolbars[i].onFirstFocus();
26243         }
26244         
26245     },
26246     
26247     // private
26248     syncValue : function()
26249     {   
26250         this.editorcore.syncValue();
26251     },
26252     
26253     pushValue : function()
26254     {   
26255         this.editorcore.pushValue();
26256     }
26257      
26258     
26259     // hide stuff that is not compatible
26260     /**
26261      * @event blur
26262      * @hide
26263      */
26264     /**
26265      * @event change
26266      * @hide
26267      */
26268     /**
26269      * @event focus
26270      * @hide
26271      */
26272     /**
26273      * @event specialkey
26274      * @hide
26275      */
26276     /**
26277      * @cfg {String} fieldClass @hide
26278      */
26279     /**
26280      * @cfg {String} focusClass @hide
26281      */
26282     /**
26283      * @cfg {String} autoCreate @hide
26284      */
26285     /**
26286      * @cfg {String} inputType @hide
26287      */
26288      
26289     /**
26290      * @cfg {String} invalidText @hide
26291      */
26292     /**
26293      * @cfg {String} msgFx @hide
26294      */
26295     /**
26296      * @cfg {String} validateOnBlur @hide
26297      */
26298 });
26299  
26300     
26301    
26302    
26303    
26304       
26305 Roo.namespace('Roo.bootstrap.htmleditor');
26306 /**
26307  * @class Roo.bootstrap.HtmlEditorToolbar1
26308  * Basic Toolbar
26309  * 
26310  * @example
26311  * Usage:
26312  *
26313  new Roo.bootstrap.HtmlEditor({
26314     ....
26315     toolbars : [
26316         new Roo.bootstrap.HtmlEditorToolbar1({
26317             disable : { fonts: 1 , format: 1, ..., ... , ...],
26318             btns : [ .... ]
26319         })
26320     }
26321      
26322  * 
26323  * @cfg {Object} disable List of elements to disable..
26324  * @cfg {Array} btns List of additional buttons.
26325  * 
26326  * 
26327  * NEEDS Extra CSS? 
26328  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26329  */
26330  
26331 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26332 {
26333     
26334     Roo.apply(this, config);
26335     
26336     // default disabled, based on 'good practice'..
26337     this.disable = this.disable || {};
26338     Roo.applyIf(this.disable, {
26339         fontSize : true,
26340         colors : true,
26341         specialElements : true
26342     });
26343     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26344     
26345     this.editor = config.editor;
26346     this.editorcore = config.editor.editorcore;
26347     
26348     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26349     
26350     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26351     // dont call parent... till later.
26352 }
26353 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26354      
26355     bar : true,
26356     
26357     editor : false,
26358     editorcore : false,
26359     
26360     
26361     formats : [
26362         "p" ,  
26363         "h1","h2","h3","h4","h5","h6", 
26364         "pre", "code", 
26365         "abbr", "acronym", "address", "cite", "samp", "var",
26366         'div','span'
26367     ],
26368     
26369     onRender : function(ct, position)
26370     {
26371        // Roo.log("Call onRender: " + this.xtype);
26372         
26373        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26374        Roo.log(this.el);
26375        this.el.dom.style.marginBottom = '0';
26376        var _this = this;
26377        var editorcore = this.editorcore;
26378        var editor= this.editor;
26379        
26380        var children = [];
26381        var btn = function(id,cmd , toggle, handler, html){
26382        
26383             var  event = toggle ? 'toggle' : 'click';
26384        
26385             var a = {
26386                 size : 'sm',
26387                 xtype: 'Button',
26388                 xns: Roo.bootstrap,
26389                 //glyphicon : id,
26390                 fa: id,
26391                 cmd : id || cmd,
26392                 enableToggle:toggle !== false,
26393                 html : html || '',
26394                 pressed : toggle ? false : null,
26395                 listeners : {}
26396             };
26397             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26398                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26399             };
26400             children.push(a);
26401             return a;
26402        }
26403        
26404     //    var cb_box = function...
26405         
26406         var style = {
26407                 xtype: 'Button',
26408                 size : 'sm',
26409                 xns: Roo.bootstrap,
26410                 fa : 'font',
26411                 //html : 'submit'
26412                 menu : {
26413                     xtype: 'Menu',
26414                     xns: Roo.bootstrap,
26415                     items:  []
26416                 }
26417         };
26418         Roo.each(this.formats, function(f) {
26419             style.menu.items.push({
26420                 xtype :'MenuItem',
26421                 xns: Roo.bootstrap,
26422                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26423                 tagname : f,
26424                 listeners : {
26425                     click : function()
26426                     {
26427                         editorcore.insertTag(this.tagname);
26428                         editor.focus();
26429                     }
26430                 }
26431                 
26432             });
26433         });
26434         children.push(style);   
26435         
26436         btn('bold',false,true);
26437         btn('italic',false,true);
26438         btn('align-left', 'justifyleft',true);
26439         btn('align-center', 'justifycenter',true);
26440         btn('align-right' , 'justifyright',true);
26441         btn('link', false, false, function(btn) {
26442             //Roo.log("create link?");
26443             var url = prompt(this.createLinkText, this.defaultLinkValue);
26444             if(url && url != 'http:/'+'/'){
26445                 this.editorcore.relayCmd('createlink', url);
26446             }
26447         }),
26448         btn('list','insertunorderedlist',true);
26449         btn('pencil', false,true, function(btn){
26450                 Roo.log(this);
26451                 this.toggleSourceEdit(btn.pressed);
26452         });
26453         
26454         if (this.editor.btns.length > 0) {
26455             for (var i = 0; i<this.editor.btns.length; i++) {
26456                 children.push(this.editor.btns[i]);
26457             }
26458         }
26459         
26460         /*
26461         var cog = {
26462                 xtype: 'Button',
26463                 size : 'sm',
26464                 xns: Roo.bootstrap,
26465                 glyphicon : 'cog',
26466                 //html : 'submit'
26467                 menu : {
26468                     xtype: 'Menu',
26469                     xns: Roo.bootstrap,
26470                     items:  []
26471                 }
26472         };
26473         
26474         cog.menu.items.push({
26475             xtype :'MenuItem',
26476             xns: Roo.bootstrap,
26477             html : Clean styles,
26478             tagname : f,
26479             listeners : {
26480                 click : function()
26481                 {
26482                     editorcore.insertTag(this.tagname);
26483                     editor.focus();
26484                 }
26485             }
26486             
26487         });
26488        */
26489         
26490          
26491        this.xtype = 'NavSimplebar';
26492         
26493         for(var i=0;i< children.length;i++) {
26494             
26495             this.buttons.add(this.addxtypeChild(children[i]));
26496             
26497         }
26498         
26499         editor.on('editorevent', this.updateToolbar, this);
26500     },
26501     onBtnClick : function(id)
26502     {
26503        this.editorcore.relayCmd(id);
26504        this.editorcore.focus();
26505     },
26506     
26507     /**
26508      * Protected method that will not generally be called directly. It triggers
26509      * a toolbar update by reading the markup state of the current selection in the editor.
26510      */
26511     updateToolbar: function(){
26512
26513         if(!this.editorcore.activated){
26514             this.editor.onFirstFocus(); // is this neeed?
26515             return;
26516         }
26517
26518         var btns = this.buttons; 
26519         var doc = this.editorcore.doc;
26520         btns.get('bold').setActive(doc.queryCommandState('bold'));
26521         btns.get('italic').setActive(doc.queryCommandState('italic'));
26522         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26523         
26524         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26525         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26526         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26527         
26528         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26529         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26530          /*
26531         
26532         var ans = this.editorcore.getAllAncestors();
26533         if (this.formatCombo) {
26534             
26535             
26536             var store = this.formatCombo.store;
26537             this.formatCombo.setValue("");
26538             for (var i =0; i < ans.length;i++) {
26539                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26540                     // select it..
26541                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26542                     break;
26543                 }
26544             }
26545         }
26546         
26547         
26548         
26549         // hides menus... - so this cant be on a menu...
26550         Roo.bootstrap.MenuMgr.hideAll();
26551         */
26552         Roo.bootstrap.MenuMgr.hideAll();
26553         //this.editorsyncValue();
26554     },
26555     onFirstFocus: function() {
26556         this.buttons.each(function(item){
26557            item.enable();
26558         });
26559     },
26560     toggleSourceEdit : function(sourceEditMode){
26561         
26562           
26563         if(sourceEditMode){
26564             Roo.log("disabling buttons");
26565            this.buttons.each( function(item){
26566                 if(item.cmd != 'pencil'){
26567                     item.disable();
26568                 }
26569             });
26570           
26571         }else{
26572             Roo.log("enabling buttons");
26573             if(this.editorcore.initialized){
26574                 this.buttons.each( function(item){
26575                     item.enable();
26576                 });
26577             }
26578             
26579         }
26580         Roo.log("calling toggole on editor");
26581         // tell the editor that it's been pressed..
26582         this.editor.toggleSourceEdit(sourceEditMode);
26583        
26584     }
26585 });
26586
26587
26588
26589
26590  
26591 /*
26592  * - LGPL
26593  */
26594
26595 /**
26596  * @class Roo.bootstrap.Markdown
26597  * @extends Roo.bootstrap.TextArea
26598  * Bootstrap Showdown editable area
26599  * @cfg {string} content
26600  * 
26601  * @constructor
26602  * Create a new Showdown
26603  */
26604
26605 Roo.bootstrap.Markdown = function(config){
26606     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26607    
26608 };
26609
26610 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26611     
26612     editing :false,
26613     
26614     initEvents : function()
26615     {
26616         
26617         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26618         this.markdownEl = this.el.createChild({
26619             cls : 'roo-markdown-area'
26620         });
26621         this.inputEl().addClass('d-none');
26622         if (this.getValue() == '') {
26623             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26624             
26625         } else {
26626             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26627         }
26628         this.markdownEl.on('click', this.toggleTextEdit, this);
26629         this.on('blur', this.toggleTextEdit, this);
26630         this.on('specialkey', this.resizeTextArea, this);
26631     },
26632     
26633     toggleTextEdit : function()
26634     {
26635         var sh = this.markdownEl.getHeight();
26636         this.inputEl().addClass('d-none');
26637         this.markdownEl.addClass('d-none');
26638         if (!this.editing) {
26639             // show editor?
26640             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26641             this.inputEl().removeClass('d-none');
26642             this.inputEl().focus();
26643             this.editing = true;
26644             return;
26645         }
26646         // show showdown...
26647         this.updateMarkdown();
26648         this.markdownEl.removeClass('d-none');
26649         this.editing = false;
26650         return;
26651     },
26652     updateMarkdown : function()
26653     {
26654         if (this.getValue() == '') {
26655             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26656             return;
26657         }
26658  
26659         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26660     },
26661     
26662     resizeTextArea: function () {
26663         
26664         var sh = 100;
26665         Roo.log([sh, this.getValue().split("\n").length * 30]);
26666         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26667     },
26668     setValue : function(val)
26669     {
26670         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26671         if (!this.editing) {
26672             this.updateMarkdown();
26673         }
26674         
26675     },
26676     focus : function()
26677     {
26678         if (!this.editing) {
26679             this.toggleTextEdit();
26680         }
26681         
26682     }
26683
26684
26685 });
26686 /**
26687  * @class Roo.bootstrap.Table.AbstractSelectionModel
26688  * @extends Roo.util.Observable
26689  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26690  * implemented by descendant classes.  This class should not be directly instantiated.
26691  * @constructor
26692  */
26693 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26694     this.locked = false;
26695     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26696 };
26697
26698
26699 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26700     /** @ignore Called by the grid automatically. Do not call directly. */
26701     init : function(grid){
26702         this.grid = grid;
26703         this.initEvents();
26704     },
26705
26706     /**
26707      * Locks the selections.
26708      */
26709     lock : function(){
26710         this.locked = true;
26711     },
26712
26713     /**
26714      * Unlocks the selections.
26715      */
26716     unlock : function(){
26717         this.locked = false;
26718     },
26719
26720     /**
26721      * Returns true if the selections are locked.
26722      * @return {Boolean}
26723      */
26724     isLocked : function(){
26725         return this.locked;
26726     },
26727     
26728     
26729     initEvents : function ()
26730     {
26731         
26732     }
26733 });
26734 /**
26735  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26736  * @class Roo.bootstrap.Table.RowSelectionModel
26737  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26738  * It supports multiple selections and keyboard selection/navigation. 
26739  * @constructor
26740  * @param {Object} config
26741  */
26742
26743 Roo.bootstrap.Table.RowSelectionModel = function(config){
26744     Roo.apply(this, config);
26745     this.selections = new Roo.util.MixedCollection(false, function(o){
26746         return o.id;
26747     });
26748
26749     this.last = false;
26750     this.lastActive = false;
26751
26752     this.addEvents({
26753         /**
26754              * @event selectionchange
26755              * Fires when the selection changes
26756              * @param {SelectionModel} this
26757              */
26758             "selectionchange" : true,
26759         /**
26760              * @event afterselectionchange
26761              * Fires after the selection changes (eg. by key press or clicking)
26762              * @param {SelectionModel} this
26763              */
26764             "afterselectionchange" : true,
26765         /**
26766              * @event beforerowselect
26767              * Fires when a row is selected being selected, return false to cancel.
26768              * @param {SelectionModel} this
26769              * @param {Number} rowIndex The selected index
26770              * @param {Boolean} keepExisting False if other selections will be cleared
26771              */
26772             "beforerowselect" : true,
26773         /**
26774              * @event rowselect
26775              * Fires when a row is selected.
26776              * @param {SelectionModel} this
26777              * @param {Number} rowIndex The selected index
26778              * @param {Roo.data.Record} r The record
26779              */
26780             "rowselect" : true,
26781         /**
26782              * @event rowdeselect
26783              * Fires when a row is deselected.
26784              * @param {SelectionModel} this
26785              * @param {Number} rowIndex The selected index
26786              */
26787         "rowdeselect" : true
26788     });
26789     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26790     this.locked = false;
26791  };
26792
26793 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26794     /**
26795      * @cfg {Boolean} singleSelect
26796      * True to allow selection of only one row at a time (defaults to false)
26797      */
26798     singleSelect : false,
26799
26800     // private
26801     initEvents : function()
26802     {
26803
26804         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26805         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26806         //}else{ // allow click to work like normal
26807          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26808         //}
26809         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26810         this.grid.on("rowclick", this.handleMouseDown, this);
26811         
26812         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26813             "up" : function(e){
26814                 if(!e.shiftKey){
26815                     this.selectPrevious(e.shiftKey);
26816                 }else if(this.last !== false && this.lastActive !== false){
26817                     var last = this.last;
26818                     this.selectRange(this.last,  this.lastActive-1);
26819                     this.grid.getView().focusRow(this.lastActive);
26820                     if(last !== false){
26821                         this.last = last;
26822                     }
26823                 }else{
26824                     this.selectFirstRow();
26825                 }
26826                 this.fireEvent("afterselectionchange", this);
26827             },
26828             "down" : function(e){
26829                 if(!e.shiftKey){
26830                     this.selectNext(e.shiftKey);
26831                 }else if(this.last !== false && this.lastActive !== false){
26832                     var last = this.last;
26833                     this.selectRange(this.last,  this.lastActive+1);
26834                     this.grid.getView().focusRow(this.lastActive);
26835                     if(last !== false){
26836                         this.last = last;
26837                     }
26838                 }else{
26839                     this.selectFirstRow();
26840                 }
26841                 this.fireEvent("afterselectionchange", this);
26842             },
26843             scope: this
26844         });
26845         this.grid.store.on('load', function(){
26846             this.selections.clear();
26847         },this);
26848         /*
26849         var view = this.grid.view;
26850         view.on("refresh", this.onRefresh, this);
26851         view.on("rowupdated", this.onRowUpdated, this);
26852         view.on("rowremoved", this.onRemove, this);
26853         */
26854     },
26855
26856     // private
26857     onRefresh : function()
26858     {
26859         var ds = this.grid.store, i, v = this.grid.view;
26860         var s = this.selections;
26861         s.each(function(r){
26862             if((i = ds.indexOfId(r.id)) != -1){
26863                 v.onRowSelect(i);
26864             }else{
26865                 s.remove(r);
26866             }
26867         });
26868     },
26869
26870     // private
26871     onRemove : function(v, index, r){
26872         this.selections.remove(r);
26873     },
26874
26875     // private
26876     onRowUpdated : function(v, index, r){
26877         if(this.isSelected(r)){
26878             v.onRowSelect(index);
26879         }
26880     },
26881
26882     /**
26883      * Select records.
26884      * @param {Array} records The records to select
26885      * @param {Boolean} keepExisting (optional) True to keep existing selections
26886      */
26887     selectRecords : function(records, keepExisting)
26888     {
26889         if(!keepExisting){
26890             this.clearSelections();
26891         }
26892             var ds = this.grid.store;
26893         for(var i = 0, len = records.length; i < len; i++){
26894             this.selectRow(ds.indexOf(records[i]), true);
26895         }
26896     },
26897
26898     /**
26899      * Gets the number of selected rows.
26900      * @return {Number}
26901      */
26902     getCount : function(){
26903         return this.selections.length;
26904     },
26905
26906     /**
26907      * Selects the first row in the grid.
26908      */
26909     selectFirstRow : function(){
26910         this.selectRow(0);
26911     },
26912
26913     /**
26914      * Select the last row.
26915      * @param {Boolean} keepExisting (optional) True to keep existing selections
26916      */
26917     selectLastRow : function(keepExisting){
26918         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26919         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26920     },
26921
26922     /**
26923      * Selects the row immediately following the last selected row.
26924      * @param {Boolean} keepExisting (optional) True to keep existing selections
26925      */
26926     selectNext : function(keepExisting)
26927     {
26928             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26929             this.selectRow(this.last+1, keepExisting);
26930             this.grid.getView().focusRow(this.last);
26931         }
26932     },
26933
26934     /**
26935      * Selects the row that precedes the last selected row.
26936      * @param {Boolean} keepExisting (optional) True to keep existing selections
26937      */
26938     selectPrevious : function(keepExisting){
26939         if(this.last){
26940             this.selectRow(this.last-1, keepExisting);
26941             this.grid.getView().focusRow(this.last);
26942         }
26943     },
26944
26945     /**
26946      * Returns the selected records
26947      * @return {Array} Array of selected records
26948      */
26949     getSelections : function(){
26950         return [].concat(this.selections.items);
26951     },
26952
26953     /**
26954      * Returns the first selected record.
26955      * @return {Record}
26956      */
26957     getSelected : function(){
26958         return this.selections.itemAt(0);
26959     },
26960
26961
26962     /**
26963      * Clears all selections.
26964      */
26965     clearSelections : function(fast)
26966     {
26967         if(this.locked) {
26968             return;
26969         }
26970         if(fast !== true){
26971                 var ds = this.grid.store;
26972             var s = this.selections;
26973             s.each(function(r){
26974                 this.deselectRow(ds.indexOfId(r.id));
26975             }, this);
26976             s.clear();
26977         }else{
26978             this.selections.clear();
26979         }
26980         this.last = false;
26981     },
26982
26983
26984     /**
26985      * Selects all rows.
26986      */
26987     selectAll : function(){
26988         if(this.locked) {
26989             return;
26990         }
26991         this.selections.clear();
26992         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26993             this.selectRow(i, true);
26994         }
26995     },
26996
26997     /**
26998      * Returns True if there is a selection.
26999      * @return {Boolean}
27000      */
27001     hasSelection : function(){
27002         return this.selections.length > 0;
27003     },
27004
27005     /**
27006      * Returns True if the specified row is selected.
27007      * @param {Number/Record} record The record or index of the record to check
27008      * @return {Boolean}
27009      */
27010     isSelected : function(index){
27011             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27012         return (r && this.selections.key(r.id) ? true : false);
27013     },
27014
27015     /**
27016      * Returns True if the specified record id is selected.
27017      * @param {String} id The id of record to check
27018      * @return {Boolean}
27019      */
27020     isIdSelected : function(id){
27021         return (this.selections.key(id) ? true : false);
27022     },
27023
27024
27025     // private
27026     handleMouseDBClick : function(e, t){
27027         
27028     },
27029     // private
27030     handleMouseDown : function(e, t)
27031     {
27032             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27033         if(this.isLocked() || rowIndex < 0 ){
27034             return;
27035         };
27036         if(e.shiftKey && this.last !== false){
27037             var last = this.last;
27038             this.selectRange(last, rowIndex, e.ctrlKey);
27039             this.last = last; // reset the last
27040             t.focus();
27041     
27042         }else{
27043             var isSelected = this.isSelected(rowIndex);
27044             //Roo.log("select row:" + rowIndex);
27045             if(isSelected){
27046                 this.deselectRow(rowIndex);
27047             } else {
27048                         this.selectRow(rowIndex, true);
27049             }
27050     
27051             /*
27052                 if(e.button !== 0 && isSelected){
27053                 alert('rowIndex 2: ' + rowIndex);
27054                     view.focusRow(rowIndex);
27055                 }else if(e.ctrlKey && isSelected){
27056                     this.deselectRow(rowIndex);
27057                 }else if(!isSelected){
27058                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27059                     view.focusRow(rowIndex);
27060                 }
27061             */
27062         }
27063         this.fireEvent("afterselectionchange", this);
27064     },
27065     // private
27066     handleDragableRowClick :  function(grid, rowIndex, e) 
27067     {
27068         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27069             this.selectRow(rowIndex, false);
27070             grid.view.focusRow(rowIndex);
27071              this.fireEvent("afterselectionchange", this);
27072         }
27073     },
27074     
27075     /**
27076      * Selects multiple rows.
27077      * @param {Array} rows Array of the indexes of the row to select
27078      * @param {Boolean} keepExisting (optional) True to keep existing selections
27079      */
27080     selectRows : function(rows, keepExisting){
27081         if(!keepExisting){
27082             this.clearSelections();
27083         }
27084         for(var i = 0, len = rows.length; i < len; i++){
27085             this.selectRow(rows[i], true);
27086         }
27087     },
27088
27089     /**
27090      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27091      * @param {Number} startRow The index of the first row in the range
27092      * @param {Number} endRow The index of the last row in the range
27093      * @param {Boolean} keepExisting (optional) True to retain existing selections
27094      */
27095     selectRange : function(startRow, endRow, keepExisting){
27096         if(this.locked) {
27097             return;
27098         }
27099         if(!keepExisting){
27100             this.clearSelections();
27101         }
27102         if(startRow <= endRow){
27103             for(var i = startRow; i <= endRow; i++){
27104                 this.selectRow(i, true);
27105             }
27106         }else{
27107             for(var i = startRow; i >= endRow; i--){
27108                 this.selectRow(i, true);
27109             }
27110         }
27111     },
27112
27113     /**
27114      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27115      * @param {Number} startRow The index of the first row in the range
27116      * @param {Number} endRow The index of the last row in the range
27117      */
27118     deselectRange : function(startRow, endRow, preventViewNotify){
27119         if(this.locked) {
27120             return;
27121         }
27122         for(var i = startRow; i <= endRow; i++){
27123             this.deselectRow(i, preventViewNotify);
27124         }
27125     },
27126
27127     /**
27128      * Selects a row.
27129      * @param {Number} row The index of the row to select
27130      * @param {Boolean} keepExisting (optional) True to keep existing selections
27131      */
27132     selectRow : function(index, keepExisting, preventViewNotify)
27133     {
27134             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27135             return;
27136         }
27137         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27138             if(!keepExisting || this.singleSelect){
27139                 this.clearSelections();
27140             }
27141             
27142             var r = this.grid.store.getAt(index);
27143             //console.log('selectRow - record id :' + r.id);
27144             
27145             this.selections.add(r);
27146             this.last = this.lastActive = index;
27147             if(!preventViewNotify){
27148                 var proxy = new Roo.Element(
27149                                 this.grid.getRowDom(index)
27150                 );
27151                 proxy.addClass('bg-info info');
27152             }
27153             this.fireEvent("rowselect", this, index, r);
27154             this.fireEvent("selectionchange", this);
27155         }
27156     },
27157
27158     /**
27159      * Deselects a row.
27160      * @param {Number} row The index of the row to deselect
27161      */
27162     deselectRow : function(index, preventViewNotify)
27163     {
27164         if(this.locked) {
27165             return;
27166         }
27167         if(this.last == index){
27168             this.last = false;
27169         }
27170         if(this.lastActive == index){
27171             this.lastActive = false;
27172         }
27173         
27174         var r = this.grid.store.getAt(index);
27175         if (!r) {
27176             return;
27177         }
27178         
27179         this.selections.remove(r);
27180         //.console.log('deselectRow - record id :' + r.id);
27181         if(!preventViewNotify){
27182         
27183             var proxy = new Roo.Element(
27184                 this.grid.getRowDom(index)
27185             );
27186             proxy.removeClass('bg-info info');
27187         }
27188         this.fireEvent("rowdeselect", this, index);
27189         this.fireEvent("selectionchange", this);
27190     },
27191
27192     // private
27193     restoreLast : function(){
27194         if(this._last){
27195             this.last = this._last;
27196         }
27197     },
27198
27199     // private
27200     acceptsNav : function(row, col, cm){
27201         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27202     },
27203
27204     // private
27205     onEditorKey : function(field, e){
27206         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27207         if(k == e.TAB){
27208             e.stopEvent();
27209             ed.completeEdit();
27210             if(e.shiftKey){
27211                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27212             }else{
27213                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27214             }
27215         }else if(k == e.ENTER && !e.ctrlKey){
27216             e.stopEvent();
27217             ed.completeEdit();
27218             if(e.shiftKey){
27219                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27220             }else{
27221                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27222             }
27223         }else if(k == e.ESC){
27224             ed.cancelEdit();
27225         }
27226         if(newCell){
27227             g.startEditing(newCell[0], newCell[1]);
27228         }
27229     }
27230 });
27231 /*
27232  * Based on:
27233  * Ext JS Library 1.1.1
27234  * Copyright(c) 2006-2007, Ext JS, LLC.
27235  *
27236  * Originally Released Under LGPL - original licence link has changed is not relivant.
27237  *
27238  * Fork - LGPL
27239  * <script type="text/javascript">
27240  */
27241  
27242 /**
27243  * @class Roo.bootstrap.PagingToolbar
27244  * @extends Roo.bootstrap.NavSimplebar
27245  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27246  * @constructor
27247  * Create a new PagingToolbar
27248  * @param {Object} config The config object
27249  * @param {Roo.data.Store} store
27250  */
27251 Roo.bootstrap.PagingToolbar = function(config)
27252 {
27253     // old args format still supported... - xtype is prefered..
27254         // created from xtype...
27255     
27256     this.ds = config.dataSource;
27257     
27258     if (config.store && !this.ds) {
27259         this.store= Roo.factory(config.store, Roo.data);
27260         this.ds = this.store;
27261         this.ds.xmodule = this.xmodule || false;
27262     }
27263     
27264     this.toolbarItems = [];
27265     if (config.items) {
27266         this.toolbarItems = config.items;
27267     }
27268     
27269     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27270     
27271     this.cursor = 0;
27272     
27273     if (this.ds) { 
27274         this.bind(this.ds);
27275     }
27276     
27277     if (Roo.bootstrap.version == 4) {
27278         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27279     } else {
27280         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27281     }
27282     
27283 };
27284
27285 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27286     /**
27287      * @cfg {Roo.data.Store} dataSource
27288      * The underlying data store providing the paged data
27289      */
27290     /**
27291      * @cfg {String/HTMLElement/Element} container
27292      * container The id or element that will contain the toolbar
27293      */
27294     /**
27295      * @cfg {Boolean} displayInfo
27296      * True to display the displayMsg (defaults to false)
27297      */
27298     /**
27299      * @cfg {Number} pageSize
27300      * The number of records to display per page (defaults to 20)
27301      */
27302     pageSize: 20,
27303     /**
27304      * @cfg {String} displayMsg
27305      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27306      */
27307     displayMsg : 'Displaying {0} - {1} of {2}',
27308     /**
27309      * @cfg {String} emptyMsg
27310      * The message to display when no records are found (defaults to "No data to display")
27311      */
27312     emptyMsg : 'No data to display',
27313     /**
27314      * Customizable piece of the default paging text (defaults to "Page")
27315      * @type String
27316      */
27317     beforePageText : "Page",
27318     /**
27319      * Customizable piece of the default paging text (defaults to "of %0")
27320      * @type String
27321      */
27322     afterPageText : "of {0}",
27323     /**
27324      * Customizable piece of the default paging text (defaults to "First Page")
27325      * @type String
27326      */
27327     firstText : "First Page",
27328     /**
27329      * Customizable piece of the default paging text (defaults to "Previous Page")
27330      * @type String
27331      */
27332     prevText : "Previous Page",
27333     /**
27334      * Customizable piece of the default paging text (defaults to "Next Page")
27335      * @type String
27336      */
27337     nextText : "Next Page",
27338     /**
27339      * Customizable piece of the default paging text (defaults to "Last Page")
27340      * @type String
27341      */
27342     lastText : "Last Page",
27343     /**
27344      * Customizable piece of the default paging text (defaults to "Refresh")
27345      * @type String
27346      */
27347     refreshText : "Refresh",
27348
27349     buttons : false,
27350     // private
27351     onRender : function(ct, position) 
27352     {
27353         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27354         this.navgroup.parentId = this.id;
27355         this.navgroup.onRender(this.el, null);
27356         // add the buttons to the navgroup
27357         
27358         if(this.displayInfo){
27359             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27360             this.displayEl = this.el.select('.x-paging-info', true).first();
27361 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27362 //            this.displayEl = navel.el.select('span',true).first();
27363         }
27364         
27365         var _this = this;
27366         
27367         if(this.buttons){
27368             Roo.each(_this.buttons, function(e){ // this might need to use render????
27369                Roo.factory(e).render(_this.el);
27370             });
27371         }
27372             
27373         Roo.each(_this.toolbarItems, function(e) {
27374             _this.navgroup.addItem(e);
27375         });
27376         
27377         
27378         this.first = this.navgroup.addItem({
27379             tooltip: this.firstText,
27380             cls: "prev btn-outline-secondary",
27381             html : ' <i class="fa fa-step-backward"></i>',
27382             disabled: true,
27383             preventDefault: true,
27384             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27385         });
27386         
27387         this.prev =  this.navgroup.addItem({
27388             tooltip: this.prevText,
27389             cls: "prev btn-outline-secondary",
27390             html : ' <i class="fa fa-backward"></i>',
27391             disabled: true,
27392             preventDefault: true,
27393             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27394         });
27395     //this.addSeparator();
27396         
27397         
27398         var field = this.navgroup.addItem( {
27399             tagtype : 'span',
27400             cls : 'x-paging-position  btn-outline-secondary',
27401              disabled: true,
27402             html : this.beforePageText  +
27403                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27404                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27405          } ); //?? escaped?
27406         
27407         this.field = field.el.select('input', true).first();
27408         this.field.on("keydown", this.onPagingKeydown, this);
27409         this.field.on("focus", function(){this.dom.select();});
27410     
27411     
27412         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27413         //this.field.setHeight(18);
27414         //this.addSeparator();
27415         this.next = this.navgroup.addItem({
27416             tooltip: this.nextText,
27417             cls: "next btn-outline-secondary",
27418             html : ' <i class="fa fa-forward"></i>',
27419             disabled: true,
27420             preventDefault: true,
27421             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27422         });
27423         this.last = this.navgroup.addItem({
27424             tooltip: this.lastText,
27425             html : ' <i class="fa fa-step-forward"></i>',
27426             cls: "next btn-outline-secondary",
27427             disabled: true,
27428             preventDefault: true,
27429             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27430         });
27431     //this.addSeparator();
27432         this.loading = this.navgroup.addItem({
27433             tooltip: this.refreshText,
27434             cls: "btn-outline-secondary",
27435             html : ' <i class="fa fa-refresh"></i>',
27436             preventDefault: true,
27437             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27438         });
27439         
27440     },
27441
27442     // private
27443     updateInfo : function(){
27444         if(this.displayEl){
27445             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27446             var msg = count == 0 ?
27447                 this.emptyMsg :
27448                 String.format(
27449                     this.displayMsg,
27450                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27451                 );
27452             this.displayEl.update(msg);
27453         }
27454     },
27455
27456     // private
27457     onLoad : function(ds, r, o)
27458     {
27459         this.cursor = o.params && o.params.start ? o.params.start : 0;
27460         
27461         var d = this.getPageData(),
27462             ap = d.activePage,
27463             ps = d.pages;
27464         
27465         
27466         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27467         this.field.dom.value = ap;
27468         this.first.setDisabled(ap == 1);
27469         this.prev.setDisabled(ap == 1);
27470         this.next.setDisabled(ap == ps);
27471         this.last.setDisabled(ap == ps);
27472         this.loading.enable();
27473         this.updateInfo();
27474     },
27475
27476     // private
27477     getPageData : function(){
27478         var total = this.ds.getTotalCount();
27479         return {
27480             total : total,
27481             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27482             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27483         };
27484     },
27485
27486     // private
27487     onLoadError : function(){
27488         this.loading.enable();
27489     },
27490
27491     // private
27492     onPagingKeydown : function(e){
27493         var k = e.getKey();
27494         var d = this.getPageData();
27495         if(k == e.RETURN){
27496             var v = this.field.dom.value, pageNum;
27497             if(!v || isNaN(pageNum = parseInt(v, 10))){
27498                 this.field.dom.value = d.activePage;
27499                 return;
27500             }
27501             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27502             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27503             e.stopEvent();
27504         }
27505         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))
27506         {
27507           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27508           this.field.dom.value = pageNum;
27509           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27510           e.stopEvent();
27511         }
27512         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27513         {
27514           var v = this.field.dom.value, pageNum; 
27515           var increment = (e.shiftKey) ? 10 : 1;
27516           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27517                 increment *= -1;
27518           }
27519           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27520             this.field.dom.value = d.activePage;
27521             return;
27522           }
27523           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27524           {
27525             this.field.dom.value = parseInt(v, 10) + increment;
27526             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27527             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27528           }
27529           e.stopEvent();
27530         }
27531     },
27532
27533     // private
27534     beforeLoad : function(){
27535         if(this.loading){
27536             this.loading.disable();
27537         }
27538     },
27539
27540     // private
27541     onClick : function(which){
27542         
27543         var ds = this.ds;
27544         if (!ds) {
27545             return;
27546         }
27547         
27548         switch(which){
27549             case "first":
27550                 ds.load({params:{start: 0, limit: this.pageSize}});
27551             break;
27552             case "prev":
27553                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27554             break;
27555             case "next":
27556                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27557             break;
27558             case "last":
27559                 var total = ds.getTotalCount();
27560                 var extra = total % this.pageSize;
27561                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27562                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27563             break;
27564             case "refresh":
27565                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27566             break;
27567         }
27568     },
27569
27570     /**
27571      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27572      * @param {Roo.data.Store} store The data store to unbind
27573      */
27574     unbind : function(ds){
27575         ds.un("beforeload", this.beforeLoad, this);
27576         ds.un("load", this.onLoad, this);
27577         ds.un("loadexception", this.onLoadError, this);
27578         ds.un("remove", this.updateInfo, this);
27579         ds.un("add", this.updateInfo, this);
27580         this.ds = undefined;
27581     },
27582
27583     /**
27584      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27585      * @param {Roo.data.Store} store The data store to bind
27586      */
27587     bind : function(ds){
27588         ds.on("beforeload", this.beforeLoad, this);
27589         ds.on("load", this.onLoad, this);
27590         ds.on("loadexception", this.onLoadError, this);
27591         ds.on("remove", this.updateInfo, this);
27592         ds.on("add", this.updateInfo, this);
27593         this.ds = ds;
27594     }
27595 });/*
27596  * - LGPL
27597  *
27598  * element
27599  * 
27600  */
27601
27602 /**
27603  * @class Roo.bootstrap.MessageBar
27604  * @extends Roo.bootstrap.Component
27605  * Bootstrap MessageBar class
27606  * @cfg {String} html contents of the MessageBar
27607  * @cfg {String} weight (info | success | warning | danger) default info
27608  * @cfg {String} beforeClass insert the bar before the given class
27609  * @cfg {Boolean} closable (true | false) default false
27610  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27611  * 
27612  * @constructor
27613  * Create a new Element
27614  * @param {Object} config The config object
27615  */
27616
27617 Roo.bootstrap.MessageBar = function(config){
27618     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27619 };
27620
27621 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27622     
27623     html: '',
27624     weight: 'info',
27625     closable: false,
27626     fixed: false,
27627     beforeClass: 'bootstrap-sticky-wrap',
27628     
27629     getAutoCreate : function(){
27630         
27631         var cfg = {
27632             tag: 'div',
27633             cls: 'alert alert-dismissable alert-' + this.weight,
27634             cn: [
27635                 {
27636                     tag: 'span',
27637                     cls: 'message',
27638                     html: this.html || ''
27639                 }
27640             ]
27641         };
27642         
27643         if(this.fixed){
27644             cfg.cls += ' alert-messages-fixed';
27645         }
27646         
27647         if(this.closable){
27648             cfg.cn.push({
27649                 tag: 'button',
27650                 cls: 'close',
27651                 html: 'x'
27652             });
27653         }
27654         
27655         return cfg;
27656     },
27657     
27658     onRender : function(ct, position)
27659     {
27660         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27661         
27662         if(!this.el){
27663             var cfg = Roo.apply({},  this.getAutoCreate());
27664             cfg.id = Roo.id();
27665             
27666             if (this.cls) {
27667                 cfg.cls += ' ' + this.cls;
27668             }
27669             if (this.style) {
27670                 cfg.style = this.style;
27671             }
27672             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27673             
27674             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27675         }
27676         
27677         this.el.select('>button.close').on('click', this.hide, this);
27678         
27679     },
27680     
27681     show : function()
27682     {
27683         if (!this.rendered) {
27684             this.render();
27685         }
27686         
27687         this.el.show();
27688         
27689         this.fireEvent('show', this);
27690         
27691     },
27692     
27693     hide : function()
27694     {
27695         if (!this.rendered) {
27696             this.render();
27697         }
27698         
27699         this.el.hide();
27700         
27701         this.fireEvent('hide', this);
27702     },
27703     
27704     update : function()
27705     {
27706 //        var e = this.el.dom.firstChild;
27707 //        
27708 //        if(this.closable){
27709 //            e = e.nextSibling;
27710 //        }
27711 //        
27712 //        e.data = this.html || '';
27713
27714         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27715     }
27716    
27717 });
27718
27719  
27720
27721      /*
27722  * - LGPL
27723  *
27724  * Graph
27725  * 
27726  */
27727
27728
27729 /**
27730  * @class Roo.bootstrap.Graph
27731  * @extends Roo.bootstrap.Component
27732  * Bootstrap Graph class
27733 > Prameters
27734  -sm {number} sm 4
27735  -md {number} md 5
27736  @cfg {String} graphtype  bar | vbar | pie
27737  @cfg {number} g_x coodinator | centre x (pie)
27738  @cfg {number} g_y coodinator | centre y (pie)
27739  @cfg {number} g_r radius (pie)
27740  @cfg {number} g_height height of the chart (respected by all elements in the set)
27741  @cfg {number} g_width width of the chart (respected by all elements in the set)
27742  @cfg {Object} title The title of the chart
27743     
27744  -{Array}  values
27745  -opts (object) options for the chart 
27746      o {
27747      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27748      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27749      o vgutter (number)
27750      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.
27751      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27752      o to
27753      o stretch (boolean)
27754      o }
27755  -opts (object) options for the pie
27756      o{
27757      o cut
27758      o startAngle (number)
27759      o endAngle (number)
27760      } 
27761  *
27762  * @constructor
27763  * Create a new Input
27764  * @param {Object} config The config object
27765  */
27766
27767 Roo.bootstrap.Graph = function(config){
27768     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27769     
27770     this.addEvents({
27771         // img events
27772         /**
27773          * @event click
27774          * The img click event for the img.
27775          * @param {Roo.EventObject} e
27776          */
27777         "click" : true
27778     });
27779 };
27780
27781 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27782     
27783     sm: 4,
27784     md: 5,
27785     graphtype: 'bar',
27786     g_height: 250,
27787     g_width: 400,
27788     g_x: 50,
27789     g_y: 50,
27790     g_r: 30,
27791     opts:{
27792         //g_colors: this.colors,
27793         g_type: 'soft',
27794         g_gutter: '20%'
27795
27796     },
27797     title : false,
27798
27799     getAutoCreate : function(){
27800         
27801         var cfg = {
27802             tag: 'div',
27803             html : null
27804         };
27805         
27806         
27807         return  cfg;
27808     },
27809
27810     onRender : function(ct,position){
27811         
27812         
27813         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27814         
27815         if (typeof(Raphael) == 'undefined') {
27816             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27817             return;
27818         }
27819         
27820         this.raphael = Raphael(this.el.dom);
27821         
27822                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27823                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27824                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27825                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27826                 /*
27827                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27828                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27829                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27830                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27831                 
27832                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27833                 r.barchart(330, 10, 300, 220, data1);
27834                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27835                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27836                 */
27837                 
27838                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27839                 // r.barchart(30, 30, 560, 250,  xdata, {
27840                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27841                 //     axis : "0 0 1 1",
27842                 //     axisxlabels :  xdata
27843                 //     //yvalues : cols,
27844                    
27845                 // });
27846 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27847 //        
27848 //        this.load(null,xdata,{
27849 //                axis : "0 0 1 1",
27850 //                axisxlabels :  xdata
27851 //                });
27852
27853     },
27854
27855     load : function(graphtype,xdata,opts)
27856     {
27857         this.raphael.clear();
27858         if(!graphtype) {
27859             graphtype = this.graphtype;
27860         }
27861         if(!opts){
27862             opts = this.opts;
27863         }
27864         var r = this.raphael,
27865             fin = function () {
27866                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27867             },
27868             fout = function () {
27869                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27870             },
27871             pfin = function() {
27872                 this.sector.stop();
27873                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27874
27875                 if (this.label) {
27876                     this.label[0].stop();
27877                     this.label[0].attr({ r: 7.5 });
27878                     this.label[1].attr({ "font-weight": 800 });
27879                 }
27880             },
27881             pfout = function() {
27882                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27883
27884                 if (this.label) {
27885                     this.label[0].animate({ r: 5 }, 500, "bounce");
27886                     this.label[1].attr({ "font-weight": 400 });
27887                 }
27888             };
27889
27890         switch(graphtype){
27891             case 'bar':
27892                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27893                 break;
27894             case 'hbar':
27895                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27896                 break;
27897             case 'pie':
27898 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27899 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27900 //            
27901                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27902                 
27903                 break;
27904
27905         }
27906         
27907         if(this.title){
27908             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27909         }
27910         
27911     },
27912     
27913     setTitle: function(o)
27914     {
27915         this.title = o;
27916     },
27917     
27918     initEvents: function() {
27919         
27920         if(!this.href){
27921             this.el.on('click', this.onClick, this);
27922         }
27923     },
27924     
27925     onClick : function(e)
27926     {
27927         Roo.log('img onclick');
27928         this.fireEvent('click', this, e);
27929     }
27930    
27931 });
27932
27933  
27934 /*
27935  * - LGPL
27936  *
27937  * numberBox
27938  * 
27939  */
27940 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27941
27942 /**
27943  * @class Roo.bootstrap.dash.NumberBox
27944  * @extends Roo.bootstrap.Component
27945  * Bootstrap NumberBox class
27946  * @cfg {String} headline Box headline
27947  * @cfg {String} content Box content
27948  * @cfg {String} icon Box icon
27949  * @cfg {String} footer Footer text
27950  * @cfg {String} fhref Footer href
27951  * 
27952  * @constructor
27953  * Create a new NumberBox
27954  * @param {Object} config The config object
27955  */
27956
27957
27958 Roo.bootstrap.dash.NumberBox = function(config){
27959     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27960     
27961 };
27962
27963 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27964     
27965     headline : '',
27966     content : '',
27967     icon : '',
27968     footer : '',
27969     fhref : '',
27970     ficon : '',
27971     
27972     getAutoCreate : function(){
27973         
27974         var cfg = {
27975             tag : 'div',
27976             cls : 'small-box ',
27977             cn : [
27978                 {
27979                     tag : 'div',
27980                     cls : 'inner',
27981                     cn :[
27982                         {
27983                             tag : 'h3',
27984                             cls : 'roo-headline',
27985                             html : this.headline
27986                         },
27987                         {
27988                             tag : 'p',
27989                             cls : 'roo-content',
27990                             html : this.content
27991                         }
27992                     ]
27993                 }
27994             ]
27995         };
27996         
27997         if(this.icon){
27998             cfg.cn.push({
27999                 tag : 'div',
28000                 cls : 'icon',
28001                 cn :[
28002                     {
28003                         tag : 'i',
28004                         cls : 'ion ' + this.icon
28005                     }
28006                 ]
28007             });
28008         }
28009         
28010         if(this.footer){
28011             var footer = {
28012                 tag : 'a',
28013                 cls : 'small-box-footer',
28014                 href : this.fhref || '#',
28015                 html : this.footer
28016             };
28017             
28018             cfg.cn.push(footer);
28019             
28020         }
28021         
28022         return  cfg;
28023     },
28024
28025     onRender : function(ct,position){
28026         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28027
28028
28029        
28030                 
28031     },
28032
28033     setHeadline: function (value)
28034     {
28035         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28036     },
28037     
28038     setFooter: function (value, href)
28039     {
28040         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28041         
28042         if(href){
28043             this.el.select('a.small-box-footer',true).first().attr('href', href);
28044         }
28045         
28046     },
28047
28048     setContent: function (value)
28049     {
28050         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28051     },
28052
28053     initEvents: function() 
28054     {   
28055         
28056     }
28057     
28058 });
28059
28060  
28061 /*
28062  * - LGPL
28063  *
28064  * TabBox
28065  * 
28066  */
28067 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28068
28069 /**
28070  * @class Roo.bootstrap.dash.TabBox
28071  * @extends Roo.bootstrap.Component
28072  * Bootstrap TabBox class
28073  * @cfg {String} title Title of the TabBox
28074  * @cfg {String} icon Icon of the TabBox
28075  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28076  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28077  * 
28078  * @constructor
28079  * Create a new TabBox
28080  * @param {Object} config The config object
28081  */
28082
28083
28084 Roo.bootstrap.dash.TabBox = function(config){
28085     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28086     this.addEvents({
28087         // raw events
28088         /**
28089          * @event addpane
28090          * When a pane is added
28091          * @param {Roo.bootstrap.dash.TabPane} pane
28092          */
28093         "addpane" : true,
28094         /**
28095          * @event activatepane
28096          * When a pane is activated
28097          * @param {Roo.bootstrap.dash.TabPane} pane
28098          */
28099         "activatepane" : true
28100         
28101          
28102     });
28103     
28104     this.panes = [];
28105 };
28106
28107 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28108
28109     title : '',
28110     icon : false,
28111     showtabs : true,
28112     tabScrollable : false,
28113     
28114     getChildContainer : function()
28115     {
28116         return this.el.select('.tab-content', true).first();
28117     },
28118     
28119     getAutoCreate : function(){
28120         
28121         var header = {
28122             tag: 'li',
28123             cls: 'pull-left header',
28124             html: this.title,
28125             cn : []
28126         };
28127         
28128         if(this.icon){
28129             header.cn.push({
28130                 tag: 'i',
28131                 cls: 'fa ' + this.icon
28132             });
28133         }
28134         
28135         var h = {
28136             tag: 'ul',
28137             cls: 'nav nav-tabs pull-right',
28138             cn: [
28139                 header
28140             ]
28141         };
28142         
28143         if(this.tabScrollable){
28144             h = {
28145                 tag: 'div',
28146                 cls: 'tab-header',
28147                 cn: [
28148                     {
28149                         tag: 'ul',
28150                         cls: 'nav nav-tabs pull-right',
28151                         cn: [
28152                             header
28153                         ]
28154                     }
28155                 ]
28156             };
28157         }
28158         
28159         var cfg = {
28160             tag: 'div',
28161             cls: 'nav-tabs-custom',
28162             cn: [
28163                 h,
28164                 {
28165                     tag: 'div',
28166                     cls: 'tab-content no-padding',
28167                     cn: []
28168                 }
28169             ]
28170         };
28171
28172         return  cfg;
28173     },
28174     initEvents : function()
28175     {
28176         //Roo.log('add add pane handler');
28177         this.on('addpane', this.onAddPane, this);
28178     },
28179      /**
28180      * Updates the box title
28181      * @param {String} html to set the title to.
28182      */
28183     setTitle : function(value)
28184     {
28185         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28186     },
28187     onAddPane : function(pane)
28188     {
28189         this.panes.push(pane);
28190         //Roo.log('addpane');
28191         //Roo.log(pane);
28192         // tabs are rendere left to right..
28193         if(!this.showtabs){
28194             return;
28195         }
28196         
28197         var ctr = this.el.select('.nav-tabs', true).first();
28198          
28199          
28200         var existing = ctr.select('.nav-tab',true);
28201         var qty = existing.getCount();;
28202         
28203         
28204         var tab = ctr.createChild({
28205             tag : 'li',
28206             cls : 'nav-tab' + (qty ? '' : ' active'),
28207             cn : [
28208                 {
28209                     tag : 'a',
28210                     href:'#',
28211                     html : pane.title
28212                 }
28213             ]
28214         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28215         pane.tab = tab;
28216         
28217         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28218         if (!qty) {
28219             pane.el.addClass('active');
28220         }
28221         
28222                 
28223     },
28224     onTabClick : function(ev,un,ob,pane)
28225     {
28226         //Roo.log('tab - prev default');
28227         ev.preventDefault();
28228         
28229         
28230         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28231         pane.tab.addClass('active');
28232         //Roo.log(pane.title);
28233         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28234         // technically we should have a deactivate event.. but maybe add later.
28235         // and it should not de-activate the selected tab...
28236         this.fireEvent('activatepane', pane);
28237         pane.el.addClass('active');
28238         pane.fireEvent('activate');
28239         
28240         
28241     },
28242     
28243     getActivePane : function()
28244     {
28245         var r = false;
28246         Roo.each(this.panes, function(p) {
28247             if(p.el.hasClass('active')){
28248                 r = p;
28249                 return false;
28250             }
28251             
28252             return;
28253         });
28254         
28255         return r;
28256     }
28257     
28258     
28259 });
28260
28261  
28262 /*
28263  * - LGPL
28264  *
28265  * Tab pane
28266  * 
28267  */
28268 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28269 /**
28270  * @class Roo.bootstrap.TabPane
28271  * @extends Roo.bootstrap.Component
28272  * Bootstrap TabPane class
28273  * @cfg {Boolean} active (false | true) Default false
28274  * @cfg {String} title title of panel
28275
28276  * 
28277  * @constructor
28278  * Create a new TabPane
28279  * @param {Object} config The config object
28280  */
28281
28282 Roo.bootstrap.dash.TabPane = function(config){
28283     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28284     
28285     this.addEvents({
28286         // raw events
28287         /**
28288          * @event activate
28289          * When a pane is activated
28290          * @param {Roo.bootstrap.dash.TabPane} pane
28291          */
28292         "activate" : true
28293          
28294     });
28295 };
28296
28297 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28298     
28299     active : false,
28300     title : '',
28301     
28302     // the tabBox that this is attached to.
28303     tab : false,
28304      
28305     getAutoCreate : function() 
28306     {
28307         var cfg = {
28308             tag: 'div',
28309             cls: 'tab-pane'
28310         };
28311         
28312         if(this.active){
28313             cfg.cls += ' active';
28314         }
28315         
28316         return cfg;
28317     },
28318     initEvents  : function()
28319     {
28320         //Roo.log('trigger add pane handler');
28321         this.parent().fireEvent('addpane', this)
28322     },
28323     
28324      /**
28325      * Updates the tab title 
28326      * @param {String} html to set the title to.
28327      */
28328     setTitle: function(str)
28329     {
28330         if (!this.tab) {
28331             return;
28332         }
28333         this.title = str;
28334         this.tab.select('a', true).first().dom.innerHTML = str;
28335         
28336     }
28337     
28338     
28339     
28340 });
28341
28342  
28343
28344
28345  /*
28346  * - LGPL
28347  *
28348  * menu
28349  * 
28350  */
28351 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28352
28353 /**
28354  * @class Roo.bootstrap.menu.Menu
28355  * @extends Roo.bootstrap.Component
28356  * Bootstrap Menu class - container for Menu
28357  * @cfg {String} html Text of the menu
28358  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28359  * @cfg {String} icon Font awesome icon
28360  * @cfg {String} pos Menu align to (top | bottom) default bottom
28361  * 
28362  * 
28363  * @constructor
28364  * Create a new Menu
28365  * @param {Object} config The config object
28366  */
28367
28368
28369 Roo.bootstrap.menu.Menu = function(config){
28370     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28371     
28372     this.addEvents({
28373         /**
28374          * @event beforeshow
28375          * Fires before this menu is displayed
28376          * @param {Roo.bootstrap.menu.Menu} this
28377          */
28378         beforeshow : true,
28379         /**
28380          * @event beforehide
28381          * Fires before this menu is hidden
28382          * @param {Roo.bootstrap.menu.Menu} this
28383          */
28384         beforehide : true,
28385         /**
28386          * @event show
28387          * Fires after this menu is displayed
28388          * @param {Roo.bootstrap.menu.Menu} this
28389          */
28390         show : true,
28391         /**
28392          * @event hide
28393          * Fires after this menu is hidden
28394          * @param {Roo.bootstrap.menu.Menu} this
28395          */
28396         hide : true,
28397         /**
28398          * @event click
28399          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28400          * @param {Roo.bootstrap.menu.Menu} this
28401          * @param {Roo.EventObject} e
28402          */
28403         click : true
28404     });
28405     
28406 };
28407
28408 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28409     
28410     submenu : false,
28411     html : '',
28412     weight : 'default',
28413     icon : false,
28414     pos : 'bottom',
28415     
28416     
28417     getChildContainer : function() {
28418         if(this.isSubMenu){
28419             return this.el;
28420         }
28421         
28422         return this.el.select('ul.dropdown-menu', true).first();  
28423     },
28424     
28425     getAutoCreate : function()
28426     {
28427         var text = [
28428             {
28429                 tag : 'span',
28430                 cls : 'roo-menu-text',
28431                 html : this.html
28432             }
28433         ];
28434         
28435         if(this.icon){
28436             text.unshift({
28437                 tag : 'i',
28438                 cls : 'fa ' + this.icon
28439             })
28440         }
28441         
28442         
28443         var cfg = {
28444             tag : 'div',
28445             cls : 'btn-group',
28446             cn : [
28447                 {
28448                     tag : 'button',
28449                     cls : 'dropdown-button btn btn-' + this.weight,
28450                     cn : text
28451                 },
28452                 {
28453                     tag : 'button',
28454                     cls : 'dropdown-toggle btn btn-' + this.weight,
28455                     cn : [
28456                         {
28457                             tag : 'span',
28458                             cls : 'caret'
28459                         }
28460                     ]
28461                 },
28462                 {
28463                     tag : 'ul',
28464                     cls : 'dropdown-menu'
28465                 }
28466             ]
28467             
28468         };
28469         
28470         if(this.pos == 'top'){
28471             cfg.cls += ' dropup';
28472         }
28473         
28474         if(this.isSubMenu){
28475             cfg = {
28476                 tag : 'ul',
28477                 cls : 'dropdown-menu'
28478             }
28479         }
28480         
28481         return cfg;
28482     },
28483     
28484     onRender : function(ct, position)
28485     {
28486         this.isSubMenu = ct.hasClass('dropdown-submenu');
28487         
28488         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28489     },
28490     
28491     initEvents : function() 
28492     {
28493         if(this.isSubMenu){
28494             return;
28495         }
28496         
28497         this.hidden = true;
28498         
28499         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28500         this.triggerEl.on('click', this.onTriggerPress, this);
28501         
28502         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28503         this.buttonEl.on('click', this.onClick, this);
28504         
28505     },
28506     
28507     list : function()
28508     {
28509         if(this.isSubMenu){
28510             return this.el;
28511         }
28512         
28513         return this.el.select('ul.dropdown-menu', true).first();
28514     },
28515     
28516     onClick : function(e)
28517     {
28518         this.fireEvent("click", this, e);
28519     },
28520     
28521     onTriggerPress  : function(e)
28522     {   
28523         if (this.isVisible()) {
28524             this.hide();
28525         } else {
28526             this.show();
28527         }
28528     },
28529     
28530     isVisible : function(){
28531         return !this.hidden;
28532     },
28533     
28534     show : function()
28535     {
28536         this.fireEvent("beforeshow", this);
28537         
28538         this.hidden = false;
28539         this.el.addClass('open');
28540         
28541         Roo.get(document).on("mouseup", this.onMouseUp, this);
28542         
28543         this.fireEvent("show", this);
28544         
28545         
28546     },
28547     
28548     hide : function()
28549     {
28550         this.fireEvent("beforehide", this);
28551         
28552         this.hidden = true;
28553         this.el.removeClass('open');
28554         
28555         Roo.get(document).un("mouseup", this.onMouseUp);
28556         
28557         this.fireEvent("hide", this);
28558     },
28559     
28560     onMouseUp : function()
28561     {
28562         this.hide();
28563     }
28564     
28565 });
28566
28567  
28568  /*
28569  * - LGPL
28570  *
28571  * menu item
28572  * 
28573  */
28574 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28575
28576 /**
28577  * @class Roo.bootstrap.menu.Item
28578  * @extends Roo.bootstrap.Component
28579  * Bootstrap MenuItem class
28580  * @cfg {Boolean} submenu (true | false) default false
28581  * @cfg {String} html text of the item
28582  * @cfg {String} href the link
28583  * @cfg {Boolean} disable (true | false) default false
28584  * @cfg {Boolean} preventDefault (true | false) default true
28585  * @cfg {String} icon Font awesome icon
28586  * @cfg {String} pos Submenu align to (left | right) default right 
28587  * 
28588  * 
28589  * @constructor
28590  * Create a new Item
28591  * @param {Object} config The config object
28592  */
28593
28594
28595 Roo.bootstrap.menu.Item = function(config){
28596     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28597     this.addEvents({
28598         /**
28599          * @event mouseover
28600          * Fires when the mouse is hovering over this menu
28601          * @param {Roo.bootstrap.menu.Item} this
28602          * @param {Roo.EventObject} e
28603          */
28604         mouseover : true,
28605         /**
28606          * @event mouseout
28607          * Fires when the mouse exits this menu
28608          * @param {Roo.bootstrap.menu.Item} this
28609          * @param {Roo.EventObject} e
28610          */
28611         mouseout : true,
28612         // raw events
28613         /**
28614          * @event click
28615          * The raw click event for the entire grid.
28616          * @param {Roo.EventObject} e
28617          */
28618         click : true
28619     });
28620 };
28621
28622 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28623     
28624     submenu : false,
28625     href : '',
28626     html : '',
28627     preventDefault: true,
28628     disable : false,
28629     icon : false,
28630     pos : 'right',
28631     
28632     getAutoCreate : function()
28633     {
28634         var text = [
28635             {
28636                 tag : 'span',
28637                 cls : 'roo-menu-item-text',
28638                 html : this.html
28639             }
28640         ];
28641         
28642         if(this.icon){
28643             text.unshift({
28644                 tag : 'i',
28645                 cls : 'fa ' + this.icon
28646             })
28647         }
28648         
28649         var cfg = {
28650             tag : 'li',
28651             cn : [
28652                 {
28653                     tag : 'a',
28654                     href : this.href || '#',
28655                     cn : text
28656                 }
28657             ]
28658         };
28659         
28660         if(this.disable){
28661             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28662         }
28663         
28664         if(this.submenu){
28665             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28666             
28667             if(this.pos == 'left'){
28668                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28669             }
28670         }
28671         
28672         return cfg;
28673     },
28674     
28675     initEvents : function() 
28676     {
28677         this.el.on('mouseover', this.onMouseOver, this);
28678         this.el.on('mouseout', this.onMouseOut, this);
28679         
28680         this.el.select('a', true).first().on('click', this.onClick, this);
28681         
28682     },
28683     
28684     onClick : function(e)
28685     {
28686         if(this.preventDefault){
28687             e.preventDefault();
28688         }
28689         
28690         this.fireEvent("click", this, e);
28691     },
28692     
28693     onMouseOver : function(e)
28694     {
28695         if(this.submenu && this.pos == 'left'){
28696             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28697         }
28698         
28699         this.fireEvent("mouseover", this, e);
28700     },
28701     
28702     onMouseOut : function(e)
28703     {
28704         this.fireEvent("mouseout", this, e);
28705     }
28706 });
28707
28708  
28709
28710  /*
28711  * - LGPL
28712  *
28713  * menu separator
28714  * 
28715  */
28716 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28717
28718 /**
28719  * @class Roo.bootstrap.menu.Separator
28720  * @extends Roo.bootstrap.Component
28721  * Bootstrap Separator class
28722  * 
28723  * @constructor
28724  * Create a new Separator
28725  * @param {Object} config The config object
28726  */
28727
28728
28729 Roo.bootstrap.menu.Separator = function(config){
28730     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28731 };
28732
28733 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28734     
28735     getAutoCreate : function(){
28736         var cfg = {
28737             tag : 'li',
28738             cls: 'divider'
28739         };
28740         
28741         return cfg;
28742     }
28743    
28744 });
28745
28746  
28747
28748  /*
28749  * - LGPL
28750  *
28751  * Tooltip
28752  * 
28753  */
28754
28755 /**
28756  * @class Roo.bootstrap.Tooltip
28757  * Bootstrap Tooltip class
28758  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28759  * to determine which dom element triggers the tooltip.
28760  * 
28761  * It needs to add support for additional attributes like tooltip-position
28762  * 
28763  * @constructor
28764  * Create a new Toolti
28765  * @param {Object} config The config object
28766  */
28767
28768 Roo.bootstrap.Tooltip = function(config){
28769     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28770     
28771     this.alignment = Roo.bootstrap.Tooltip.alignment;
28772     
28773     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28774         this.alignment = config.alignment;
28775     }
28776     
28777 };
28778
28779 Roo.apply(Roo.bootstrap.Tooltip, {
28780     /**
28781      * @function init initialize tooltip monitoring.
28782      * @static
28783      */
28784     currentEl : false,
28785     currentTip : false,
28786     currentRegion : false,
28787     
28788     //  init : delay?
28789     
28790     init : function()
28791     {
28792         Roo.get(document).on('mouseover', this.enter ,this);
28793         Roo.get(document).on('mouseout', this.leave, this);
28794          
28795         
28796         this.currentTip = new Roo.bootstrap.Tooltip();
28797     },
28798     
28799     enter : function(ev)
28800     {
28801         var dom = ev.getTarget();
28802         
28803         //Roo.log(['enter',dom]);
28804         var el = Roo.fly(dom);
28805         if (this.currentEl) {
28806             //Roo.log(dom);
28807             //Roo.log(this.currentEl);
28808             //Roo.log(this.currentEl.contains(dom));
28809             if (this.currentEl == el) {
28810                 return;
28811             }
28812             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28813                 return;
28814             }
28815
28816         }
28817         
28818         if (this.currentTip.el) {
28819             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28820         }    
28821         //Roo.log(ev);
28822         
28823         if(!el || el.dom == document){
28824             return;
28825         }
28826         
28827         var bindEl = el;
28828         
28829         // you can not look for children, as if el is the body.. then everythign is the child..
28830         if (!el.attr('tooltip')) { //
28831             if (!el.select("[tooltip]").elements.length) {
28832                 return;
28833             }
28834             // is the mouse over this child...?
28835             bindEl = el.select("[tooltip]").first();
28836             var xy = ev.getXY();
28837             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28838                 //Roo.log("not in region.");
28839                 return;
28840             }
28841             //Roo.log("child element over..");
28842             
28843         }
28844         this.currentEl = bindEl;
28845         this.currentTip.bind(bindEl);
28846         this.currentRegion = Roo.lib.Region.getRegion(dom);
28847         this.currentTip.enter();
28848         
28849     },
28850     leave : function(ev)
28851     {
28852         var dom = ev.getTarget();
28853         //Roo.log(['leave',dom]);
28854         if (!this.currentEl) {
28855             return;
28856         }
28857         
28858         
28859         if (dom != this.currentEl.dom) {
28860             return;
28861         }
28862         var xy = ev.getXY();
28863         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28864             return;
28865         }
28866         // only activate leave if mouse cursor is outside... bounding box..
28867         
28868         
28869         
28870         
28871         if (this.currentTip) {
28872             this.currentTip.leave();
28873         }
28874         //Roo.log('clear currentEl');
28875         this.currentEl = false;
28876         
28877         
28878     },
28879     alignment : {
28880         'left' : ['r-l', [-2,0], 'right'],
28881         'right' : ['l-r', [2,0], 'left'],
28882         'bottom' : ['t-b', [0,2], 'top'],
28883         'top' : [ 'b-t', [0,-2], 'bottom']
28884     }
28885     
28886 });
28887
28888
28889 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28890     
28891     
28892     bindEl : false,
28893     
28894     delay : null, // can be { show : 300 , hide: 500}
28895     
28896     timeout : null,
28897     
28898     hoverState : null, //???
28899     
28900     placement : 'bottom', 
28901     
28902     alignment : false,
28903     
28904     getAutoCreate : function(){
28905     
28906         var cfg = {
28907            cls : 'tooltip',   
28908            role : 'tooltip',
28909            cn : [
28910                 {
28911                     cls : 'tooltip-arrow arrow'
28912                 },
28913                 {
28914                     cls : 'tooltip-inner'
28915                 }
28916            ]
28917         };
28918         
28919         return cfg;
28920     },
28921     bind : function(el)
28922     {
28923         this.bindEl = el;
28924     },
28925     
28926     initEvents : function()
28927     {
28928         this.arrowEl = this.el.select('.arrow', true).first();
28929         this.innerEl = this.el.select('.tooltip-inner', true).first();
28930     },
28931     
28932     enter : function () {
28933        
28934         if (this.timeout != null) {
28935             clearTimeout(this.timeout);
28936         }
28937         
28938         this.hoverState = 'in';
28939          //Roo.log("enter - show");
28940         if (!this.delay || !this.delay.show) {
28941             this.show();
28942             return;
28943         }
28944         var _t = this;
28945         this.timeout = setTimeout(function () {
28946             if (_t.hoverState == 'in') {
28947                 _t.show();
28948             }
28949         }, this.delay.show);
28950     },
28951     leave : function()
28952     {
28953         clearTimeout(this.timeout);
28954     
28955         this.hoverState = 'out';
28956          if (!this.delay || !this.delay.hide) {
28957             this.hide();
28958             return;
28959         }
28960        
28961         var _t = this;
28962         this.timeout = setTimeout(function () {
28963             //Roo.log("leave - timeout");
28964             
28965             if (_t.hoverState == 'out') {
28966                 _t.hide();
28967                 Roo.bootstrap.Tooltip.currentEl = false;
28968             }
28969         }, delay);
28970     },
28971     
28972     show : function (msg)
28973     {
28974         if (!this.el) {
28975             this.render(document.body);
28976         }
28977         // set content.
28978         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28979         
28980         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28981         
28982         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28983         
28984         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28985                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28986         
28987         var placement = typeof this.placement == 'function' ?
28988             this.placement.call(this, this.el, on_el) :
28989             this.placement;
28990             
28991         var autoToken = /\s?auto?\s?/i;
28992         var autoPlace = autoToken.test(placement);
28993         if (autoPlace) {
28994             placement = placement.replace(autoToken, '') || 'top';
28995         }
28996         
28997         //this.el.detach()
28998         //this.el.setXY([0,0]);
28999         this.el.show();
29000         //this.el.dom.style.display='block';
29001         
29002         //this.el.appendTo(on_el);
29003         
29004         var p = this.getPosition();
29005         var box = this.el.getBox();
29006         
29007         if (autoPlace) {
29008             // fixme..
29009         }
29010         
29011         var align = this.alignment[placement];
29012         
29013         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29014         
29015         if(placement == 'top' || placement == 'bottom'){
29016             if(xy[0] < 0){
29017                 placement = 'right';
29018             }
29019             
29020             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29021                 placement = 'left';
29022             }
29023             
29024             var scroll = Roo.select('body', true).first().getScroll();
29025             
29026             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29027                 placement = 'top';
29028             }
29029             
29030             align = this.alignment[placement];
29031             
29032             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29033             
29034         }
29035         
29036         this.el.alignTo(this.bindEl, align[0],align[1]);
29037         //var arrow = this.el.select('.arrow',true).first();
29038         //arrow.set(align[2], 
29039         
29040         this.el.addClass(placement);
29041         this.el.addClass("bs-tooltip-"+ placement);
29042         
29043         this.el.addClass('in fade show');
29044         
29045         this.hoverState = null;
29046         
29047         if (this.el.hasClass('fade')) {
29048             // fade it?
29049         }
29050         
29051         
29052         
29053         
29054         
29055     },
29056     hide : function()
29057     {
29058          
29059         if (!this.el) {
29060             return;
29061         }
29062         //this.el.setXY([0,0]);
29063         this.el.removeClass(['show', 'in']);
29064         //this.el.hide();
29065         
29066     }
29067     
29068 });
29069  
29070
29071  /*
29072  * - LGPL
29073  *
29074  * Location Picker
29075  * 
29076  */
29077
29078 /**
29079  * @class Roo.bootstrap.LocationPicker
29080  * @extends Roo.bootstrap.Component
29081  * Bootstrap LocationPicker class
29082  * @cfg {Number} latitude Position when init default 0
29083  * @cfg {Number} longitude Position when init default 0
29084  * @cfg {Number} zoom default 15
29085  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29086  * @cfg {Boolean} mapTypeControl default false
29087  * @cfg {Boolean} disableDoubleClickZoom default false
29088  * @cfg {Boolean} scrollwheel default true
29089  * @cfg {Boolean} streetViewControl default false
29090  * @cfg {Number} radius default 0
29091  * @cfg {String} locationName
29092  * @cfg {Boolean} draggable default true
29093  * @cfg {Boolean} enableAutocomplete default false
29094  * @cfg {Boolean} enableReverseGeocode default true
29095  * @cfg {String} markerTitle
29096  * 
29097  * @constructor
29098  * Create a new LocationPicker
29099  * @param {Object} config The config object
29100  */
29101
29102
29103 Roo.bootstrap.LocationPicker = function(config){
29104     
29105     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29106     
29107     this.addEvents({
29108         /**
29109          * @event initial
29110          * Fires when the picker initialized.
29111          * @param {Roo.bootstrap.LocationPicker} this
29112          * @param {Google Location} location
29113          */
29114         initial : true,
29115         /**
29116          * @event positionchanged
29117          * Fires when the picker position changed.
29118          * @param {Roo.bootstrap.LocationPicker} this
29119          * @param {Google Location} location
29120          */
29121         positionchanged : true,
29122         /**
29123          * @event resize
29124          * Fires when the map resize.
29125          * @param {Roo.bootstrap.LocationPicker} this
29126          */
29127         resize : true,
29128         /**
29129          * @event show
29130          * Fires when the map show.
29131          * @param {Roo.bootstrap.LocationPicker} this
29132          */
29133         show : true,
29134         /**
29135          * @event hide
29136          * Fires when the map hide.
29137          * @param {Roo.bootstrap.LocationPicker} this
29138          */
29139         hide : true,
29140         /**
29141          * @event mapClick
29142          * Fires when click the map.
29143          * @param {Roo.bootstrap.LocationPicker} this
29144          * @param {Map event} e
29145          */
29146         mapClick : true,
29147         /**
29148          * @event mapRightClick
29149          * Fires when right click the map.
29150          * @param {Roo.bootstrap.LocationPicker} this
29151          * @param {Map event} e
29152          */
29153         mapRightClick : true,
29154         /**
29155          * @event markerClick
29156          * Fires when click the marker.
29157          * @param {Roo.bootstrap.LocationPicker} this
29158          * @param {Map event} e
29159          */
29160         markerClick : true,
29161         /**
29162          * @event markerRightClick
29163          * Fires when right click the marker.
29164          * @param {Roo.bootstrap.LocationPicker} this
29165          * @param {Map event} e
29166          */
29167         markerRightClick : true,
29168         /**
29169          * @event OverlayViewDraw
29170          * Fires when OverlayView Draw
29171          * @param {Roo.bootstrap.LocationPicker} this
29172          */
29173         OverlayViewDraw : true,
29174         /**
29175          * @event OverlayViewOnAdd
29176          * Fires when OverlayView Draw
29177          * @param {Roo.bootstrap.LocationPicker} this
29178          */
29179         OverlayViewOnAdd : true,
29180         /**
29181          * @event OverlayViewOnRemove
29182          * Fires when OverlayView Draw
29183          * @param {Roo.bootstrap.LocationPicker} this
29184          */
29185         OverlayViewOnRemove : true,
29186         /**
29187          * @event OverlayViewShow
29188          * Fires when OverlayView Draw
29189          * @param {Roo.bootstrap.LocationPicker} this
29190          * @param {Pixel} cpx
29191          */
29192         OverlayViewShow : true,
29193         /**
29194          * @event OverlayViewHide
29195          * Fires when OverlayView Draw
29196          * @param {Roo.bootstrap.LocationPicker} this
29197          */
29198         OverlayViewHide : true,
29199         /**
29200          * @event loadexception
29201          * Fires when load google lib failed.
29202          * @param {Roo.bootstrap.LocationPicker} this
29203          */
29204         loadexception : true
29205     });
29206         
29207 };
29208
29209 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29210     
29211     gMapContext: false,
29212     
29213     latitude: 0,
29214     longitude: 0,
29215     zoom: 15,
29216     mapTypeId: false,
29217     mapTypeControl: false,
29218     disableDoubleClickZoom: false,
29219     scrollwheel: true,
29220     streetViewControl: false,
29221     radius: 0,
29222     locationName: '',
29223     draggable: true,
29224     enableAutocomplete: false,
29225     enableReverseGeocode: true,
29226     markerTitle: '',
29227     
29228     getAutoCreate: function()
29229     {
29230
29231         var cfg = {
29232             tag: 'div',
29233             cls: 'roo-location-picker'
29234         };
29235         
29236         return cfg
29237     },
29238     
29239     initEvents: function(ct, position)
29240     {       
29241         if(!this.el.getWidth() || this.isApplied()){
29242             return;
29243         }
29244         
29245         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29246         
29247         this.initial();
29248     },
29249     
29250     initial: function()
29251     {
29252         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29253             this.fireEvent('loadexception', this);
29254             return;
29255         }
29256         
29257         if(!this.mapTypeId){
29258             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29259         }
29260         
29261         this.gMapContext = this.GMapContext();
29262         
29263         this.initOverlayView();
29264         
29265         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29266         
29267         var _this = this;
29268                 
29269         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29270             _this.setPosition(_this.gMapContext.marker.position);
29271         });
29272         
29273         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29274             _this.fireEvent('mapClick', this, event);
29275             
29276         });
29277
29278         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29279             _this.fireEvent('mapRightClick', this, event);
29280             
29281         });
29282         
29283         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29284             _this.fireEvent('markerClick', this, event);
29285             
29286         });
29287
29288         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29289             _this.fireEvent('markerRightClick', this, event);
29290             
29291         });
29292         
29293         this.setPosition(this.gMapContext.location);
29294         
29295         this.fireEvent('initial', this, this.gMapContext.location);
29296     },
29297     
29298     initOverlayView: function()
29299     {
29300         var _this = this;
29301         
29302         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29303             
29304             draw: function()
29305             {
29306                 _this.fireEvent('OverlayViewDraw', _this);
29307             },
29308             
29309             onAdd: function()
29310             {
29311                 _this.fireEvent('OverlayViewOnAdd', _this);
29312             },
29313             
29314             onRemove: function()
29315             {
29316                 _this.fireEvent('OverlayViewOnRemove', _this);
29317             },
29318             
29319             show: function(cpx)
29320             {
29321                 _this.fireEvent('OverlayViewShow', _this, cpx);
29322             },
29323             
29324             hide: function()
29325             {
29326                 _this.fireEvent('OverlayViewHide', _this);
29327             }
29328             
29329         });
29330     },
29331     
29332     fromLatLngToContainerPixel: function(event)
29333     {
29334         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29335     },
29336     
29337     isApplied: function() 
29338     {
29339         return this.getGmapContext() == false ? false : true;
29340     },
29341     
29342     getGmapContext: function() 
29343     {
29344         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29345     },
29346     
29347     GMapContext: function() 
29348     {
29349         var position = new google.maps.LatLng(this.latitude, this.longitude);
29350         
29351         var _map = new google.maps.Map(this.el.dom, {
29352             center: position,
29353             zoom: this.zoom,
29354             mapTypeId: this.mapTypeId,
29355             mapTypeControl: this.mapTypeControl,
29356             disableDoubleClickZoom: this.disableDoubleClickZoom,
29357             scrollwheel: this.scrollwheel,
29358             streetViewControl: this.streetViewControl,
29359             locationName: this.locationName,
29360             draggable: this.draggable,
29361             enableAutocomplete: this.enableAutocomplete,
29362             enableReverseGeocode: this.enableReverseGeocode
29363         });
29364         
29365         var _marker = new google.maps.Marker({
29366             position: position,
29367             map: _map,
29368             title: this.markerTitle,
29369             draggable: this.draggable
29370         });
29371         
29372         return {
29373             map: _map,
29374             marker: _marker,
29375             circle: null,
29376             location: position,
29377             radius: this.radius,
29378             locationName: this.locationName,
29379             addressComponents: {
29380                 formatted_address: null,
29381                 addressLine1: null,
29382                 addressLine2: null,
29383                 streetName: null,
29384                 streetNumber: null,
29385                 city: null,
29386                 district: null,
29387                 state: null,
29388                 stateOrProvince: null
29389             },
29390             settings: this,
29391             domContainer: this.el.dom,
29392             geodecoder: new google.maps.Geocoder()
29393         };
29394     },
29395     
29396     drawCircle: function(center, radius, options) 
29397     {
29398         if (this.gMapContext.circle != null) {
29399             this.gMapContext.circle.setMap(null);
29400         }
29401         if (radius > 0) {
29402             radius *= 1;
29403             options = Roo.apply({}, options, {
29404                 strokeColor: "#0000FF",
29405                 strokeOpacity: .35,
29406                 strokeWeight: 2,
29407                 fillColor: "#0000FF",
29408                 fillOpacity: .2
29409             });
29410             
29411             options.map = this.gMapContext.map;
29412             options.radius = radius;
29413             options.center = center;
29414             this.gMapContext.circle = new google.maps.Circle(options);
29415             return this.gMapContext.circle;
29416         }
29417         
29418         return null;
29419     },
29420     
29421     setPosition: function(location) 
29422     {
29423         this.gMapContext.location = location;
29424         this.gMapContext.marker.setPosition(location);
29425         this.gMapContext.map.panTo(location);
29426         this.drawCircle(location, this.gMapContext.radius, {});
29427         
29428         var _this = this;
29429         
29430         if (this.gMapContext.settings.enableReverseGeocode) {
29431             this.gMapContext.geodecoder.geocode({
29432                 latLng: this.gMapContext.location
29433             }, function(results, status) {
29434                 
29435                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29436                     _this.gMapContext.locationName = results[0].formatted_address;
29437                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29438                     
29439                     _this.fireEvent('positionchanged', this, location);
29440                 }
29441             });
29442             
29443             return;
29444         }
29445         
29446         this.fireEvent('positionchanged', this, location);
29447     },
29448     
29449     resize: function()
29450     {
29451         google.maps.event.trigger(this.gMapContext.map, "resize");
29452         
29453         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29454         
29455         this.fireEvent('resize', this);
29456     },
29457     
29458     setPositionByLatLng: function(latitude, longitude)
29459     {
29460         this.setPosition(new google.maps.LatLng(latitude, longitude));
29461     },
29462     
29463     getCurrentPosition: function() 
29464     {
29465         return {
29466             latitude: this.gMapContext.location.lat(),
29467             longitude: this.gMapContext.location.lng()
29468         };
29469     },
29470     
29471     getAddressName: function() 
29472     {
29473         return this.gMapContext.locationName;
29474     },
29475     
29476     getAddressComponents: function() 
29477     {
29478         return this.gMapContext.addressComponents;
29479     },
29480     
29481     address_component_from_google_geocode: function(address_components) 
29482     {
29483         var result = {};
29484         
29485         for (var i = 0; i < address_components.length; i++) {
29486             var component = address_components[i];
29487             if (component.types.indexOf("postal_code") >= 0) {
29488                 result.postalCode = component.short_name;
29489             } else if (component.types.indexOf("street_number") >= 0) {
29490                 result.streetNumber = component.short_name;
29491             } else if (component.types.indexOf("route") >= 0) {
29492                 result.streetName = component.short_name;
29493             } else if (component.types.indexOf("neighborhood") >= 0) {
29494                 result.city = component.short_name;
29495             } else if (component.types.indexOf("locality") >= 0) {
29496                 result.city = component.short_name;
29497             } else if (component.types.indexOf("sublocality") >= 0) {
29498                 result.district = component.short_name;
29499             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29500                 result.stateOrProvince = component.short_name;
29501             } else if (component.types.indexOf("country") >= 0) {
29502                 result.country = component.short_name;
29503             }
29504         }
29505         
29506         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29507         result.addressLine2 = "";
29508         return result;
29509     },
29510     
29511     setZoomLevel: function(zoom)
29512     {
29513         this.gMapContext.map.setZoom(zoom);
29514     },
29515     
29516     show: function()
29517     {
29518         if(!this.el){
29519             return;
29520         }
29521         
29522         this.el.show();
29523         
29524         this.resize();
29525         
29526         this.fireEvent('show', this);
29527     },
29528     
29529     hide: function()
29530     {
29531         if(!this.el){
29532             return;
29533         }
29534         
29535         this.el.hide();
29536         
29537         this.fireEvent('hide', this);
29538     }
29539     
29540 });
29541
29542 Roo.apply(Roo.bootstrap.LocationPicker, {
29543     
29544     OverlayView : function(map, options)
29545     {
29546         options = options || {};
29547         
29548         this.setMap(map);
29549     }
29550     
29551     
29552 });/**
29553  * @class Roo.bootstrap.Alert
29554  * @extends Roo.bootstrap.Component
29555  * Bootstrap Alert class - shows an alert area box
29556  * eg
29557  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29558   Enter a valid email address
29559 </div>
29560  * @licence LGPL
29561  * @cfg {String} title The title of alert
29562  * @cfg {String} html The content of alert
29563  * @cfg {String} weight (  success | info | warning | danger )
29564  * @cfg {String} faicon font-awesomeicon
29565  * 
29566  * @constructor
29567  * Create a new alert
29568  * @param {Object} config The config object
29569  */
29570
29571
29572 Roo.bootstrap.Alert = function(config){
29573     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29574     
29575 };
29576
29577 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29578     
29579     title: '',
29580     html: '',
29581     weight: false,
29582     faicon: false,
29583     
29584     getAutoCreate : function()
29585     {
29586         
29587         var cfg = {
29588             tag : 'div',
29589             cls : 'alert',
29590             cn : [
29591                 {
29592                     tag : 'i',
29593                     cls : 'roo-alert-icon'
29594                     
29595                 },
29596                 {
29597                     tag : 'b',
29598                     cls : 'roo-alert-title',
29599                     html : this.title
29600                 },
29601                 {
29602                     tag : 'span',
29603                     cls : 'roo-alert-text',
29604                     html : this.html
29605                 }
29606             ]
29607         };
29608         
29609         if(this.faicon){
29610             cfg.cn[0].cls += ' fa ' + this.faicon;
29611         }
29612         
29613         if(this.weight){
29614             cfg.cls += ' alert-' + this.weight;
29615         }
29616         
29617         return cfg;
29618     },
29619     
29620     initEvents: function() 
29621     {
29622         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29623     },
29624     
29625     setTitle : function(str)
29626     {
29627         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29628     },
29629     
29630     setText : function(str)
29631     {
29632         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29633     },
29634     
29635     setWeight : function(weight)
29636     {
29637         if(this.weight){
29638             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29639         }
29640         
29641         this.weight = weight;
29642         
29643         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29644     },
29645     
29646     setIcon : function(icon)
29647     {
29648         if(this.faicon){
29649             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29650         }
29651         
29652         this.faicon = icon;
29653         
29654         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29655     },
29656     
29657     hide: function() 
29658     {
29659         this.el.hide();   
29660     },
29661     
29662     show: function() 
29663     {  
29664         this.el.show();   
29665     }
29666     
29667 });
29668
29669  
29670 /*
29671 * Licence: LGPL
29672 */
29673
29674 /**
29675  * @class Roo.bootstrap.UploadCropbox
29676  * @extends Roo.bootstrap.Component
29677  * Bootstrap UploadCropbox class
29678  * @cfg {String} emptyText show when image has been loaded
29679  * @cfg {String} rotateNotify show when image too small to rotate
29680  * @cfg {Number} errorTimeout default 3000
29681  * @cfg {Number} minWidth default 300
29682  * @cfg {Number} minHeight default 300
29683  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29684  * @cfg {Boolean} isDocument (true|false) default false
29685  * @cfg {String} url action url
29686  * @cfg {String} paramName default 'imageUpload'
29687  * @cfg {String} method default POST
29688  * @cfg {Boolean} loadMask (true|false) default true
29689  * @cfg {Boolean} loadingText default 'Loading...'
29690  * 
29691  * @constructor
29692  * Create a new UploadCropbox
29693  * @param {Object} config The config object
29694  */
29695
29696 Roo.bootstrap.UploadCropbox = function(config){
29697     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29698     
29699     this.addEvents({
29700         /**
29701          * @event beforeselectfile
29702          * Fire before select file
29703          * @param {Roo.bootstrap.UploadCropbox} this
29704          */
29705         "beforeselectfile" : true,
29706         /**
29707          * @event initial
29708          * Fire after initEvent
29709          * @param {Roo.bootstrap.UploadCropbox} this
29710          */
29711         "initial" : true,
29712         /**
29713          * @event crop
29714          * Fire after initEvent
29715          * @param {Roo.bootstrap.UploadCropbox} this
29716          * @param {String} data
29717          */
29718         "crop" : true,
29719         /**
29720          * @event prepare
29721          * Fire when preparing the file data
29722          * @param {Roo.bootstrap.UploadCropbox} this
29723          * @param {Object} file
29724          */
29725         "prepare" : true,
29726         /**
29727          * @event exception
29728          * Fire when get exception
29729          * @param {Roo.bootstrap.UploadCropbox} this
29730          * @param {XMLHttpRequest} xhr
29731          */
29732         "exception" : true,
29733         /**
29734          * @event beforeloadcanvas
29735          * Fire before load the canvas
29736          * @param {Roo.bootstrap.UploadCropbox} this
29737          * @param {String} src
29738          */
29739         "beforeloadcanvas" : true,
29740         /**
29741          * @event trash
29742          * Fire when trash image
29743          * @param {Roo.bootstrap.UploadCropbox} this
29744          */
29745         "trash" : true,
29746         /**
29747          * @event download
29748          * Fire when download the image
29749          * @param {Roo.bootstrap.UploadCropbox} this
29750          */
29751         "download" : true,
29752         /**
29753          * @event footerbuttonclick
29754          * Fire when footerbuttonclick
29755          * @param {Roo.bootstrap.UploadCropbox} this
29756          * @param {String} type
29757          */
29758         "footerbuttonclick" : true,
29759         /**
29760          * @event resize
29761          * Fire when resize
29762          * @param {Roo.bootstrap.UploadCropbox} this
29763          */
29764         "resize" : true,
29765         /**
29766          * @event rotate
29767          * Fire when rotate the image
29768          * @param {Roo.bootstrap.UploadCropbox} this
29769          * @param {String} pos
29770          */
29771         "rotate" : true,
29772         /**
29773          * @event inspect
29774          * Fire when inspect the file
29775          * @param {Roo.bootstrap.UploadCropbox} this
29776          * @param {Object} file
29777          */
29778         "inspect" : true,
29779         /**
29780          * @event upload
29781          * Fire when xhr upload the file
29782          * @param {Roo.bootstrap.UploadCropbox} this
29783          * @param {Object} data
29784          */
29785         "upload" : true,
29786         /**
29787          * @event arrange
29788          * Fire when arrange the file data
29789          * @param {Roo.bootstrap.UploadCropbox} this
29790          * @param {Object} formData
29791          */
29792         "arrange" : true
29793     });
29794     
29795     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29796 };
29797
29798 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29799     
29800     emptyText : 'Click to upload image',
29801     rotateNotify : 'Image is too small to rotate',
29802     errorTimeout : 3000,
29803     scale : 0,
29804     baseScale : 1,
29805     rotate : 0,
29806     dragable : false,
29807     pinching : false,
29808     mouseX : 0,
29809     mouseY : 0,
29810     cropData : false,
29811     minWidth : 300,
29812     minHeight : 300,
29813     file : false,
29814     exif : {},
29815     baseRotate : 1,
29816     cropType : 'image/jpeg',
29817     buttons : false,
29818     canvasLoaded : false,
29819     isDocument : false,
29820     method : 'POST',
29821     paramName : 'imageUpload',
29822     loadMask : true,
29823     loadingText : 'Loading...',
29824     maskEl : false,
29825     
29826     getAutoCreate : function()
29827     {
29828         var cfg = {
29829             tag : 'div',
29830             cls : 'roo-upload-cropbox',
29831             cn : [
29832                 {
29833                     tag : 'input',
29834                     cls : 'roo-upload-cropbox-selector',
29835                     type : 'file'
29836                 },
29837                 {
29838                     tag : 'div',
29839                     cls : 'roo-upload-cropbox-body',
29840                     style : 'cursor:pointer',
29841                     cn : [
29842                         {
29843                             tag : 'div',
29844                             cls : 'roo-upload-cropbox-preview'
29845                         },
29846                         {
29847                             tag : 'div',
29848                             cls : 'roo-upload-cropbox-thumb'
29849                         },
29850                         {
29851                             tag : 'div',
29852                             cls : 'roo-upload-cropbox-empty-notify',
29853                             html : this.emptyText
29854                         },
29855                         {
29856                             tag : 'div',
29857                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29858                             html : this.rotateNotify
29859                         }
29860                     ]
29861                 },
29862                 {
29863                     tag : 'div',
29864                     cls : 'roo-upload-cropbox-footer',
29865                     cn : {
29866                         tag : 'div',
29867                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29868                         cn : []
29869                     }
29870                 }
29871             ]
29872         };
29873         
29874         return cfg;
29875     },
29876     
29877     onRender : function(ct, position)
29878     {
29879         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29880         
29881         if (this.buttons.length) {
29882             
29883             Roo.each(this.buttons, function(bb) {
29884                 
29885                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29886                 
29887                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29888                 
29889             }, this);
29890         }
29891         
29892         if(this.loadMask){
29893             this.maskEl = this.el;
29894         }
29895     },
29896     
29897     initEvents : function()
29898     {
29899         this.urlAPI = (window.createObjectURL && window) || 
29900                                 (window.URL && URL.revokeObjectURL && URL) || 
29901                                 (window.webkitURL && webkitURL);
29902                         
29903         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29904         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29905         
29906         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29907         this.selectorEl.hide();
29908         
29909         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29910         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29911         
29912         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29913         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29914         this.thumbEl.hide();
29915         
29916         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29917         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29918         
29919         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29920         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29921         this.errorEl.hide();
29922         
29923         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29924         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29925         this.footerEl.hide();
29926         
29927         this.setThumbBoxSize();
29928         
29929         this.bind();
29930         
29931         this.resize();
29932         
29933         this.fireEvent('initial', this);
29934     },
29935
29936     bind : function()
29937     {
29938         var _this = this;
29939         
29940         window.addEventListener("resize", function() { _this.resize(); } );
29941         
29942         this.bodyEl.on('click', this.beforeSelectFile, this);
29943         
29944         if(Roo.isTouch){
29945             this.bodyEl.on('touchstart', this.onTouchStart, this);
29946             this.bodyEl.on('touchmove', this.onTouchMove, this);
29947             this.bodyEl.on('touchend', this.onTouchEnd, this);
29948         }
29949         
29950         if(!Roo.isTouch){
29951             this.bodyEl.on('mousedown', this.onMouseDown, this);
29952             this.bodyEl.on('mousemove', this.onMouseMove, this);
29953             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29954             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29955             Roo.get(document).on('mouseup', this.onMouseUp, this);
29956         }
29957         
29958         this.selectorEl.on('change', this.onFileSelected, this);
29959     },
29960     
29961     reset : function()
29962     {    
29963         this.scale = 0;
29964         this.baseScale = 1;
29965         this.rotate = 0;
29966         this.baseRotate = 1;
29967         this.dragable = false;
29968         this.pinching = false;
29969         this.mouseX = 0;
29970         this.mouseY = 0;
29971         this.cropData = false;
29972         this.notifyEl.dom.innerHTML = this.emptyText;
29973         
29974         this.selectorEl.dom.value = '';
29975         
29976     },
29977     
29978     resize : function()
29979     {
29980         if(this.fireEvent('resize', this) != false){
29981             this.setThumbBoxPosition();
29982             this.setCanvasPosition();
29983         }
29984     },
29985     
29986     onFooterButtonClick : function(e, el, o, type)
29987     {
29988         switch (type) {
29989             case 'rotate-left' :
29990                 this.onRotateLeft(e);
29991                 break;
29992             case 'rotate-right' :
29993                 this.onRotateRight(e);
29994                 break;
29995             case 'picture' :
29996                 this.beforeSelectFile(e);
29997                 break;
29998             case 'trash' :
29999                 this.trash(e);
30000                 break;
30001             case 'crop' :
30002                 this.crop(e);
30003                 break;
30004             case 'download' :
30005                 this.download(e);
30006                 break;
30007             default :
30008                 break;
30009         }
30010         
30011         this.fireEvent('footerbuttonclick', this, type);
30012     },
30013     
30014     beforeSelectFile : function(e)
30015     {
30016         e.preventDefault();
30017         
30018         if(this.fireEvent('beforeselectfile', this) != false){
30019             this.selectorEl.dom.click();
30020         }
30021     },
30022     
30023     onFileSelected : function(e)
30024     {
30025         e.preventDefault();
30026         
30027         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30028             return;
30029         }
30030         
30031         var file = this.selectorEl.dom.files[0];
30032         
30033         if(this.fireEvent('inspect', this, file) != false){
30034             this.prepare(file);
30035         }
30036         
30037     },
30038     
30039     trash : function(e)
30040     {
30041         this.fireEvent('trash', this);
30042     },
30043     
30044     download : function(e)
30045     {
30046         this.fireEvent('download', this);
30047     },
30048     
30049     loadCanvas : function(src)
30050     {   
30051         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30052             
30053             this.reset();
30054             
30055             this.imageEl = document.createElement('img');
30056             
30057             var _this = this;
30058             
30059             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30060             
30061             this.imageEl.src = src;
30062         }
30063     },
30064     
30065     onLoadCanvas : function()
30066     {   
30067         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30068         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30069         
30070         this.bodyEl.un('click', this.beforeSelectFile, this);
30071         
30072         this.notifyEl.hide();
30073         this.thumbEl.show();
30074         this.footerEl.show();
30075         
30076         this.baseRotateLevel();
30077         
30078         if(this.isDocument){
30079             this.setThumbBoxSize();
30080         }
30081         
30082         this.setThumbBoxPosition();
30083         
30084         this.baseScaleLevel();
30085         
30086         this.draw();
30087         
30088         this.resize();
30089         
30090         this.canvasLoaded = true;
30091         
30092         if(this.loadMask){
30093             this.maskEl.unmask();
30094         }
30095         
30096     },
30097     
30098     setCanvasPosition : function()
30099     {   
30100         if(!this.canvasEl){
30101             return;
30102         }
30103         
30104         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30105         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30106         
30107         this.previewEl.setLeft(pw);
30108         this.previewEl.setTop(ph);
30109         
30110     },
30111     
30112     onMouseDown : function(e)
30113     {   
30114         e.stopEvent();
30115         
30116         this.dragable = true;
30117         this.pinching = false;
30118         
30119         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30120             this.dragable = false;
30121             return;
30122         }
30123         
30124         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30125         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30126         
30127     },
30128     
30129     onMouseMove : function(e)
30130     {   
30131         e.stopEvent();
30132         
30133         if(!this.canvasLoaded){
30134             return;
30135         }
30136         
30137         if (!this.dragable){
30138             return;
30139         }
30140         
30141         var minX = Math.ceil(this.thumbEl.getLeft(true));
30142         var minY = Math.ceil(this.thumbEl.getTop(true));
30143         
30144         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30145         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30146         
30147         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30148         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30149         
30150         x = x - this.mouseX;
30151         y = y - this.mouseY;
30152         
30153         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30154         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30155         
30156         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30157         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30158         
30159         this.previewEl.setLeft(bgX);
30160         this.previewEl.setTop(bgY);
30161         
30162         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30163         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30164     },
30165     
30166     onMouseUp : function(e)
30167     {   
30168         e.stopEvent();
30169         
30170         this.dragable = false;
30171     },
30172     
30173     onMouseWheel : function(e)
30174     {   
30175         e.stopEvent();
30176         
30177         this.startScale = this.scale;
30178         
30179         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30180         
30181         if(!this.zoomable()){
30182             this.scale = this.startScale;
30183             return;
30184         }
30185         
30186         this.draw();
30187         
30188         return;
30189     },
30190     
30191     zoomable : function()
30192     {
30193         var minScale = this.thumbEl.getWidth() / this.minWidth;
30194         
30195         if(this.minWidth < this.minHeight){
30196             minScale = this.thumbEl.getHeight() / this.minHeight;
30197         }
30198         
30199         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30200         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30201         
30202         if(
30203                 this.isDocument &&
30204                 (this.rotate == 0 || this.rotate == 180) && 
30205                 (
30206                     width > this.imageEl.OriginWidth || 
30207                     height > this.imageEl.OriginHeight ||
30208                     (width < this.minWidth && height < this.minHeight)
30209                 )
30210         ){
30211             return false;
30212         }
30213         
30214         if(
30215                 this.isDocument &&
30216                 (this.rotate == 90 || this.rotate == 270) && 
30217                 (
30218                     width > this.imageEl.OriginWidth || 
30219                     height > this.imageEl.OriginHeight ||
30220                     (width < this.minHeight && height < this.minWidth)
30221                 )
30222         ){
30223             return false;
30224         }
30225         
30226         if(
30227                 !this.isDocument &&
30228                 (this.rotate == 0 || this.rotate == 180) && 
30229                 (
30230                     width < this.minWidth || 
30231                     width > this.imageEl.OriginWidth || 
30232                     height < this.minHeight || 
30233                     height > this.imageEl.OriginHeight
30234                 )
30235         ){
30236             return false;
30237         }
30238         
30239         if(
30240                 !this.isDocument &&
30241                 (this.rotate == 90 || this.rotate == 270) && 
30242                 (
30243                     width < this.minHeight || 
30244                     width > this.imageEl.OriginWidth || 
30245                     height < this.minWidth || 
30246                     height > this.imageEl.OriginHeight
30247                 )
30248         ){
30249             return false;
30250         }
30251         
30252         return true;
30253         
30254     },
30255     
30256     onRotateLeft : function(e)
30257     {   
30258         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30259             
30260             var minScale = this.thumbEl.getWidth() / this.minWidth;
30261             
30262             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30263             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30264             
30265             this.startScale = this.scale;
30266             
30267             while (this.getScaleLevel() < minScale){
30268             
30269                 this.scale = this.scale + 1;
30270                 
30271                 if(!this.zoomable()){
30272                     break;
30273                 }
30274                 
30275                 if(
30276                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30277                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30278                 ){
30279                     continue;
30280                 }
30281                 
30282                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30283
30284                 this.draw();
30285                 
30286                 return;
30287             }
30288             
30289             this.scale = this.startScale;
30290             
30291             this.onRotateFail();
30292             
30293             return false;
30294         }
30295         
30296         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30297
30298         if(this.isDocument){
30299             this.setThumbBoxSize();
30300             this.setThumbBoxPosition();
30301             this.setCanvasPosition();
30302         }
30303         
30304         this.draw();
30305         
30306         this.fireEvent('rotate', this, 'left');
30307         
30308     },
30309     
30310     onRotateRight : function(e)
30311     {
30312         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30313             
30314             var minScale = this.thumbEl.getWidth() / this.minWidth;
30315         
30316             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30317             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30318             
30319             this.startScale = this.scale;
30320             
30321             while (this.getScaleLevel() < minScale){
30322             
30323                 this.scale = this.scale + 1;
30324                 
30325                 if(!this.zoomable()){
30326                     break;
30327                 }
30328                 
30329                 if(
30330                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30331                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30332                 ){
30333                     continue;
30334                 }
30335                 
30336                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30337
30338                 this.draw();
30339                 
30340                 return;
30341             }
30342             
30343             this.scale = this.startScale;
30344             
30345             this.onRotateFail();
30346             
30347             return false;
30348         }
30349         
30350         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30351
30352         if(this.isDocument){
30353             this.setThumbBoxSize();
30354             this.setThumbBoxPosition();
30355             this.setCanvasPosition();
30356         }
30357         
30358         this.draw();
30359         
30360         this.fireEvent('rotate', this, 'right');
30361     },
30362     
30363     onRotateFail : function()
30364     {
30365         this.errorEl.show(true);
30366         
30367         var _this = this;
30368         
30369         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30370     },
30371     
30372     draw : function()
30373     {
30374         this.previewEl.dom.innerHTML = '';
30375         
30376         var canvasEl = document.createElement("canvas");
30377         
30378         var contextEl = canvasEl.getContext("2d");
30379         
30380         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30381         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30382         var center = this.imageEl.OriginWidth / 2;
30383         
30384         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30385             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30386             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30387             center = this.imageEl.OriginHeight / 2;
30388         }
30389         
30390         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30391         
30392         contextEl.translate(center, center);
30393         contextEl.rotate(this.rotate * Math.PI / 180);
30394
30395         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30396         
30397         this.canvasEl = document.createElement("canvas");
30398         
30399         this.contextEl = this.canvasEl.getContext("2d");
30400         
30401         switch (this.rotate) {
30402             case 0 :
30403                 
30404                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30405                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30406                 
30407                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30408                 
30409                 break;
30410             case 90 : 
30411                 
30412                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30413                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30414                 
30415                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30416                     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);
30417                     break;
30418                 }
30419                 
30420                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30421                 
30422                 break;
30423             case 180 :
30424                 
30425                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30426                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30427                 
30428                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30429                     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);
30430                     break;
30431                 }
30432                 
30433                 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);
30434                 
30435                 break;
30436             case 270 :
30437                 
30438                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30439                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30440         
30441                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30442                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30443                     break;
30444                 }
30445                 
30446                 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);
30447                 
30448                 break;
30449             default : 
30450                 break;
30451         }
30452         
30453         this.previewEl.appendChild(this.canvasEl);
30454         
30455         this.setCanvasPosition();
30456     },
30457     
30458     crop : function()
30459     {
30460         if(!this.canvasLoaded){
30461             return;
30462         }
30463         
30464         var imageCanvas = document.createElement("canvas");
30465         
30466         var imageContext = imageCanvas.getContext("2d");
30467         
30468         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30469         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30470         
30471         var center = imageCanvas.width / 2;
30472         
30473         imageContext.translate(center, center);
30474         
30475         imageContext.rotate(this.rotate * Math.PI / 180);
30476         
30477         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30478         
30479         var canvas = document.createElement("canvas");
30480         
30481         var context = canvas.getContext("2d");
30482                 
30483         canvas.width = this.minWidth;
30484         canvas.height = this.minHeight;
30485
30486         switch (this.rotate) {
30487             case 0 :
30488                 
30489                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30490                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30491                 
30492                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30493                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30494                 
30495                 var targetWidth = this.minWidth - 2 * x;
30496                 var targetHeight = this.minHeight - 2 * y;
30497                 
30498                 var scale = 1;
30499                 
30500                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30501                     scale = targetWidth / width;
30502                 }
30503                 
30504                 if(x > 0 && y == 0){
30505                     scale = targetHeight / height;
30506                 }
30507                 
30508                 if(x > 0 && y > 0){
30509                     scale = targetWidth / width;
30510                     
30511                     if(width < height){
30512                         scale = targetHeight / height;
30513                     }
30514                 }
30515                 
30516                 context.scale(scale, scale);
30517                 
30518                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30519                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30520
30521                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30522                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30523
30524                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30525                 
30526                 break;
30527             case 90 : 
30528                 
30529                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30530                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30531                 
30532                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30533                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30534                 
30535                 var targetWidth = this.minWidth - 2 * x;
30536                 var targetHeight = this.minHeight - 2 * y;
30537                 
30538                 var scale = 1;
30539                 
30540                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30541                     scale = targetWidth / width;
30542                 }
30543                 
30544                 if(x > 0 && y == 0){
30545                     scale = targetHeight / height;
30546                 }
30547                 
30548                 if(x > 0 && y > 0){
30549                     scale = targetWidth / width;
30550                     
30551                     if(width < height){
30552                         scale = targetHeight / height;
30553                     }
30554                 }
30555                 
30556                 context.scale(scale, scale);
30557                 
30558                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30559                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30560
30561                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30562                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30563                 
30564                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30565                 
30566                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30567                 
30568                 break;
30569             case 180 :
30570                 
30571                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30572                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30573                 
30574                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30575                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30576                 
30577                 var targetWidth = this.minWidth - 2 * x;
30578                 var targetHeight = this.minHeight - 2 * y;
30579                 
30580                 var scale = 1;
30581                 
30582                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30583                     scale = targetWidth / width;
30584                 }
30585                 
30586                 if(x > 0 && y == 0){
30587                     scale = targetHeight / height;
30588                 }
30589                 
30590                 if(x > 0 && y > 0){
30591                     scale = targetWidth / width;
30592                     
30593                     if(width < height){
30594                         scale = targetHeight / height;
30595                     }
30596                 }
30597                 
30598                 context.scale(scale, scale);
30599                 
30600                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30601                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30602
30603                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30604                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30605
30606                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30607                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30608                 
30609                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30610                 
30611                 break;
30612             case 270 :
30613                 
30614                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30615                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30616                 
30617                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30618                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30619                 
30620                 var targetWidth = this.minWidth - 2 * x;
30621                 var targetHeight = this.minHeight - 2 * y;
30622                 
30623                 var scale = 1;
30624                 
30625                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30626                     scale = targetWidth / width;
30627                 }
30628                 
30629                 if(x > 0 && y == 0){
30630                     scale = targetHeight / height;
30631                 }
30632                 
30633                 if(x > 0 && y > 0){
30634                     scale = targetWidth / width;
30635                     
30636                     if(width < height){
30637                         scale = targetHeight / height;
30638                     }
30639                 }
30640                 
30641                 context.scale(scale, scale);
30642                 
30643                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30644                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30645
30646                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30647                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30648                 
30649                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30650                 
30651                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30652                 
30653                 break;
30654             default : 
30655                 break;
30656         }
30657         
30658         this.cropData = canvas.toDataURL(this.cropType);
30659         
30660         if(this.fireEvent('crop', this, this.cropData) !== false){
30661             this.process(this.file, this.cropData);
30662         }
30663         
30664         return;
30665         
30666     },
30667     
30668     setThumbBoxSize : function()
30669     {
30670         var width, height;
30671         
30672         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30673             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30674             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30675             
30676             this.minWidth = width;
30677             this.minHeight = height;
30678             
30679             if(this.rotate == 90 || this.rotate == 270){
30680                 this.minWidth = height;
30681                 this.minHeight = width;
30682             }
30683         }
30684         
30685         height = 300;
30686         width = Math.ceil(this.minWidth * height / this.minHeight);
30687         
30688         if(this.minWidth > this.minHeight){
30689             width = 300;
30690             height = Math.ceil(this.minHeight * width / this.minWidth);
30691         }
30692         
30693         this.thumbEl.setStyle({
30694             width : width + 'px',
30695             height : height + 'px'
30696         });
30697
30698         return;
30699             
30700     },
30701     
30702     setThumbBoxPosition : function()
30703     {
30704         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30705         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30706         
30707         this.thumbEl.setLeft(x);
30708         this.thumbEl.setTop(y);
30709         
30710     },
30711     
30712     baseRotateLevel : function()
30713     {
30714         this.baseRotate = 1;
30715         
30716         if(
30717                 typeof(this.exif) != 'undefined' &&
30718                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30719                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30720         ){
30721             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30722         }
30723         
30724         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30725         
30726     },
30727     
30728     baseScaleLevel : function()
30729     {
30730         var width, height;
30731         
30732         if(this.isDocument){
30733             
30734             if(this.baseRotate == 6 || this.baseRotate == 8){
30735             
30736                 height = this.thumbEl.getHeight();
30737                 this.baseScale = height / this.imageEl.OriginWidth;
30738
30739                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30740                     width = this.thumbEl.getWidth();
30741                     this.baseScale = width / this.imageEl.OriginHeight;
30742                 }
30743
30744                 return;
30745             }
30746
30747             height = this.thumbEl.getHeight();
30748             this.baseScale = height / this.imageEl.OriginHeight;
30749
30750             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30751                 width = this.thumbEl.getWidth();
30752                 this.baseScale = width / this.imageEl.OriginWidth;
30753             }
30754
30755             return;
30756         }
30757         
30758         if(this.baseRotate == 6 || this.baseRotate == 8){
30759             
30760             width = this.thumbEl.getHeight();
30761             this.baseScale = width / this.imageEl.OriginHeight;
30762             
30763             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30764                 height = this.thumbEl.getWidth();
30765                 this.baseScale = height / this.imageEl.OriginHeight;
30766             }
30767             
30768             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30769                 height = this.thumbEl.getWidth();
30770                 this.baseScale = height / this.imageEl.OriginHeight;
30771                 
30772                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30773                     width = this.thumbEl.getHeight();
30774                     this.baseScale = width / this.imageEl.OriginWidth;
30775                 }
30776             }
30777             
30778             return;
30779         }
30780         
30781         width = this.thumbEl.getWidth();
30782         this.baseScale = width / this.imageEl.OriginWidth;
30783         
30784         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30785             height = this.thumbEl.getHeight();
30786             this.baseScale = height / this.imageEl.OriginHeight;
30787         }
30788         
30789         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30790             
30791             height = this.thumbEl.getHeight();
30792             this.baseScale = height / this.imageEl.OriginHeight;
30793             
30794             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30795                 width = this.thumbEl.getWidth();
30796                 this.baseScale = width / this.imageEl.OriginWidth;
30797             }
30798             
30799         }
30800         
30801         return;
30802     },
30803     
30804     getScaleLevel : function()
30805     {
30806         return this.baseScale * Math.pow(1.1, this.scale);
30807     },
30808     
30809     onTouchStart : function(e)
30810     {
30811         if(!this.canvasLoaded){
30812             this.beforeSelectFile(e);
30813             return;
30814         }
30815         
30816         var touches = e.browserEvent.touches;
30817         
30818         if(!touches){
30819             return;
30820         }
30821         
30822         if(touches.length == 1){
30823             this.onMouseDown(e);
30824             return;
30825         }
30826         
30827         if(touches.length != 2){
30828             return;
30829         }
30830         
30831         var coords = [];
30832         
30833         for(var i = 0, finger; finger = touches[i]; i++){
30834             coords.push(finger.pageX, finger.pageY);
30835         }
30836         
30837         var x = Math.pow(coords[0] - coords[2], 2);
30838         var y = Math.pow(coords[1] - coords[3], 2);
30839         
30840         this.startDistance = Math.sqrt(x + y);
30841         
30842         this.startScale = this.scale;
30843         
30844         this.pinching = true;
30845         this.dragable = false;
30846         
30847     },
30848     
30849     onTouchMove : function(e)
30850     {
30851         if(!this.pinching && !this.dragable){
30852             return;
30853         }
30854         
30855         var touches = e.browserEvent.touches;
30856         
30857         if(!touches){
30858             return;
30859         }
30860         
30861         if(this.dragable){
30862             this.onMouseMove(e);
30863             return;
30864         }
30865         
30866         var coords = [];
30867         
30868         for(var i = 0, finger; finger = touches[i]; i++){
30869             coords.push(finger.pageX, finger.pageY);
30870         }
30871         
30872         var x = Math.pow(coords[0] - coords[2], 2);
30873         var y = Math.pow(coords[1] - coords[3], 2);
30874         
30875         this.endDistance = Math.sqrt(x + y);
30876         
30877         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30878         
30879         if(!this.zoomable()){
30880             this.scale = this.startScale;
30881             return;
30882         }
30883         
30884         this.draw();
30885         
30886     },
30887     
30888     onTouchEnd : function(e)
30889     {
30890         this.pinching = false;
30891         this.dragable = false;
30892         
30893     },
30894     
30895     process : function(file, crop)
30896     {
30897         if(this.loadMask){
30898             this.maskEl.mask(this.loadingText);
30899         }
30900         
30901         this.xhr = new XMLHttpRequest();
30902         
30903         file.xhr = this.xhr;
30904
30905         this.xhr.open(this.method, this.url, true);
30906         
30907         var headers = {
30908             "Accept": "application/json",
30909             "Cache-Control": "no-cache",
30910             "X-Requested-With": "XMLHttpRequest"
30911         };
30912         
30913         for (var headerName in headers) {
30914             var headerValue = headers[headerName];
30915             if (headerValue) {
30916                 this.xhr.setRequestHeader(headerName, headerValue);
30917             }
30918         }
30919         
30920         var _this = this;
30921         
30922         this.xhr.onload = function()
30923         {
30924             _this.xhrOnLoad(_this.xhr);
30925         }
30926         
30927         this.xhr.onerror = function()
30928         {
30929             _this.xhrOnError(_this.xhr);
30930         }
30931         
30932         var formData = new FormData();
30933
30934         formData.append('returnHTML', 'NO');
30935         
30936         if(crop){
30937             formData.append('crop', crop);
30938         }
30939         
30940         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30941             formData.append(this.paramName, file, file.name);
30942         }
30943         
30944         if(typeof(file.filename) != 'undefined'){
30945             formData.append('filename', file.filename);
30946         }
30947         
30948         if(typeof(file.mimetype) != 'undefined'){
30949             formData.append('mimetype', file.mimetype);
30950         }
30951         
30952         if(this.fireEvent('arrange', this, formData) != false){
30953             this.xhr.send(formData);
30954         };
30955     },
30956     
30957     xhrOnLoad : function(xhr)
30958     {
30959         if(this.loadMask){
30960             this.maskEl.unmask();
30961         }
30962         
30963         if (xhr.readyState !== 4) {
30964             this.fireEvent('exception', this, xhr);
30965             return;
30966         }
30967
30968         var response = Roo.decode(xhr.responseText);
30969         
30970         if(!response.success){
30971             this.fireEvent('exception', this, xhr);
30972             return;
30973         }
30974         
30975         var response = Roo.decode(xhr.responseText);
30976         
30977         this.fireEvent('upload', this, response);
30978         
30979     },
30980     
30981     xhrOnError : function()
30982     {
30983         if(this.loadMask){
30984             this.maskEl.unmask();
30985         }
30986         
30987         Roo.log('xhr on error');
30988         
30989         var response = Roo.decode(xhr.responseText);
30990           
30991         Roo.log(response);
30992         
30993     },
30994     
30995     prepare : function(file)
30996     {   
30997         if(this.loadMask){
30998             this.maskEl.mask(this.loadingText);
30999         }
31000         
31001         this.file = false;
31002         this.exif = {};
31003         
31004         if(typeof(file) === 'string'){
31005             this.loadCanvas(file);
31006             return;
31007         }
31008         
31009         if(!file || !this.urlAPI){
31010             return;
31011         }
31012         
31013         this.file = file;
31014         this.cropType = file.type;
31015         
31016         var _this = this;
31017         
31018         if(this.fireEvent('prepare', this, this.file) != false){
31019             
31020             var reader = new FileReader();
31021             
31022             reader.onload = function (e) {
31023                 if (e.target.error) {
31024                     Roo.log(e.target.error);
31025                     return;
31026                 }
31027                 
31028                 var buffer = e.target.result,
31029                     dataView = new DataView(buffer),
31030                     offset = 2,
31031                     maxOffset = dataView.byteLength - 4,
31032                     markerBytes,
31033                     markerLength;
31034                 
31035                 if (dataView.getUint16(0) === 0xffd8) {
31036                     while (offset < maxOffset) {
31037                         markerBytes = dataView.getUint16(offset);
31038                         
31039                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31040                             markerLength = dataView.getUint16(offset + 2) + 2;
31041                             if (offset + markerLength > dataView.byteLength) {
31042                                 Roo.log('Invalid meta data: Invalid segment size.');
31043                                 break;
31044                             }
31045                             
31046                             if(markerBytes == 0xffe1){
31047                                 _this.parseExifData(
31048                                     dataView,
31049                                     offset,
31050                                     markerLength
31051                                 );
31052                             }
31053                             
31054                             offset += markerLength;
31055                             
31056                             continue;
31057                         }
31058                         
31059                         break;
31060                     }
31061                     
31062                 }
31063                 
31064                 var url = _this.urlAPI.createObjectURL(_this.file);
31065                 
31066                 _this.loadCanvas(url);
31067                 
31068                 return;
31069             }
31070             
31071             reader.readAsArrayBuffer(this.file);
31072             
31073         }
31074         
31075     },
31076     
31077     parseExifData : function(dataView, offset, length)
31078     {
31079         var tiffOffset = offset + 10,
31080             littleEndian,
31081             dirOffset;
31082     
31083         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31084             // No Exif data, might be XMP data instead
31085             return;
31086         }
31087         
31088         // Check for the ASCII code for "Exif" (0x45786966):
31089         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31090             // No Exif data, might be XMP data instead
31091             return;
31092         }
31093         if (tiffOffset + 8 > dataView.byteLength) {
31094             Roo.log('Invalid Exif data: Invalid segment size.');
31095             return;
31096         }
31097         // Check for the two null bytes:
31098         if (dataView.getUint16(offset + 8) !== 0x0000) {
31099             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31100             return;
31101         }
31102         // Check the byte alignment:
31103         switch (dataView.getUint16(tiffOffset)) {
31104         case 0x4949:
31105             littleEndian = true;
31106             break;
31107         case 0x4D4D:
31108             littleEndian = false;
31109             break;
31110         default:
31111             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31112             return;
31113         }
31114         // Check for the TIFF tag marker (0x002A):
31115         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31116             Roo.log('Invalid Exif data: Missing TIFF marker.');
31117             return;
31118         }
31119         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31120         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31121         
31122         this.parseExifTags(
31123             dataView,
31124             tiffOffset,
31125             tiffOffset + dirOffset,
31126             littleEndian
31127         );
31128     },
31129     
31130     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31131     {
31132         var tagsNumber,
31133             dirEndOffset,
31134             i;
31135         if (dirOffset + 6 > dataView.byteLength) {
31136             Roo.log('Invalid Exif data: Invalid directory offset.');
31137             return;
31138         }
31139         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31140         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31141         if (dirEndOffset + 4 > dataView.byteLength) {
31142             Roo.log('Invalid Exif data: Invalid directory size.');
31143             return;
31144         }
31145         for (i = 0; i < tagsNumber; i += 1) {
31146             this.parseExifTag(
31147                 dataView,
31148                 tiffOffset,
31149                 dirOffset + 2 + 12 * i, // tag offset
31150                 littleEndian
31151             );
31152         }
31153         // Return the offset to the next directory:
31154         return dataView.getUint32(dirEndOffset, littleEndian);
31155     },
31156     
31157     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31158     {
31159         var tag = dataView.getUint16(offset, littleEndian);
31160         
31161         this.exif[tag] = this.getExifValue(
31162             dataView,
31163             tiffOffset,
31164             offset,
31165             dataView.getUint16(offset + 2, littleEndian), // tag type
31166             dataView.getUint32(offset + 4, littleEndian), // tag length
31167             littleEndian
31168         );
31169     },
31170     
31171     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31172     {
31173         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31174             tagSize,
31175             dataOffset,
31176             values,
31177             i,
31178             str,
31179             c;
31180     
31181         if (!tagType) {
31182             Roo.log('Invalid Exif data: Invalid tag type.');
31183             return;
31184         }
31185         
31186         tagSize = tagType.size * length;
31187         // Determine if the value is contained in the dataOffset bytes,
31188         // or if the value at the dataOffset is a pointer to the actual data:
31189         dataOffset = tagSize > 4 ?
31190                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31191         if (dataOffset + tagSize > dataView.byteLength) {
31192             Roo.log('Invalid Exif data: Invalid data offset.');
31193             return;
31194         }
31195         if (length === 1) {
31196             return tagType.getValue(dataView, dataOffset, littleEndian);
31197         }
31198         values = [];
31199         for (i = 0; i < length; i += 1) {
31200             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31201         }
31202         
31203         if (tagType.ascii) {
31204             str = '';
31205             // Concatenate the chars:
31206             for (i = 0; i < values.length; i += 1) {
31207                 c = values[i];
31208                 // Ignore the terminating NULL byte(s):
31209                 if (c === '\u0000') {
31210                     break;
31211                 }
31212                 str += c;
31213             }
31214             return str;
31215         }
31216         return values;
31217     }
31218     
31219 });
31220
31221 Roo.apply(Roo.bootstrap.UploadCropbox, {
31222     tags : {
31223         'Orientation': 0x0112
31224     },
31225     
31226     Orientation: {
31227             1: 0, //'top-left',
31228 //            2: 'top-right',
31229             3: 180, //'bottom-right',
31230 //            4: 'bottom-left',
31231 //            5: 'left-top',
31232             6: 90, //'right-top',
31233 //            7: 'right-bottom',
31234             8: 270 //'left-bottom'
31235     },
31236     
31237     exifTagTypes : {
31238         // byte, 8-bit unsigned int:
31239         1: {
31240             getValue: function (dataView, dataOffset) {
31241                 return dataView.getUint8(dataOffset);
31242             },
31243             size: 1
31244         },
31245         // ascii, 8-bit byte:
31246         2: {
31247             getValue: function (dataView, dataOffset) {
31248                 return String.fromCharCode(dataView.getUint8(dataOffset));
31249             },
31250             size: 1,
31251             ascii: true
31252         },
31253         // short, 16 bit int:
31254         3: {
31255             getValue: function (dataView, dataOffset, littleEndian) {
31256                 return dataView.getUint16(dataOffset, littleEndian);
31257             },
31258             size: 2
31259         },
31260         // long, 32 bit int:
31261         4: {
31262             getValue: function (dataView, dataOffset, littleEndian) {
31263                 return dataView.getUint32(dataOffset, littleEndian);
31264             },
31265             size: 4
31266         },
31267         // rational = two long values, first is numerator, second is denominator:
31268         5: {
31269             getValue: function (dataView, dataOffset, littleEndian) {
31270                 return dataView.getUint32(dataOffset, littleEndian) /
31271                     dataView.getUint32(dataOffset + 4, littleEndian);
31272             },
31273             size: 8
31274         },
31275         // slong, 32 bit signed int:
31276         9: {
31277             getValue: function (dataView, dataOffset, littleEndian) {
31278                 return dataView.getInt32(dataOffset, littleEndian);
31279             },
31280             size: 4
31281         },
31282         // srational, two slongs, first is numerator, second is denominator:
31283         10: {
31284             getValue: function (dataView, dataOffset, littleEndian) {
31285                 return dataView.getInt32(dataOffset, littleEndian) /
31286                     dataView.getInt32(dataOffset + 4, littleEndian);
31287             },
31288             size: 8
31289         }
31290     },
31291     
31292     footer : {
31293         STANDARD : [
31294             {
31295                 tag : 'div',
31296                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31297                 action : 'rotate-left',
31298                 cn : [
31299                     {
31300                         tag : 'button',
31301                         cls : 'btn btn-default',
31302                         html : '<i class="fa fa-undo"></i>'
31303                     }
31304                 ]
31305             },
31306             {
31307                 tag : 'div',
31308                 cls : 'btn-group roo-upload-cropbox-picture',
31309                 action : 'picture',
31310                 cn : [
31311                     {
31312                         tag : 'button',
31313                         cls : 'btn btn-default',
31314                         html : '<i class="fa fa-picture-o"></i>'
31315                     }
31316                 ]
31317             },
31318             {
31319                 tag : 'div',
31320                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31321                 action : 'rotate-right',
31322                 cn : [
31323                     {
31324                         tag : 'button',
31325                         cls : 'btn btn-default',
31326                         html : '<i class="fa fa-repeat"></i>'
31327                     }
31328                 ]
31329             }
31330         ],
31331         DOCUMENT : [
31332             {
31333                 tag : 'div',
31334                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31335                 action : 'rotate-left',
31336                 cn : [
31337                     {
31338                         tag : 'button',
31339                         cls : 'btn btn-default',
31340                         html : '<i class="fa fa-undo"></i>'
31341                     }
31342                 ]
31343             },
31344             {
31345                 tag : 'div',
31346                 cls : 'btn-group roo-upload-cropbox-download',
31347                 action : 'download',
31348                 cn : [
31349                     {
31350                         tag : 'button',
31351                         cls : 'btn btn-default',
31352                         html : '<i class="fa fa-download"></i>'
31353                     }
31354                 ]
31355             },
31356             {
31357                 tag : 'div',
31358                 cls : 'btn-group roo-upload-cropbox-crop',
31359                 action : 'crop',
31360                 cn : [
31361                     {
31362                         tag : 'button',
31363                         cls : 'btn btn-default',
31364                         html : '<i class="fa fa-crop"></i>'
31365                     }
31366                 ]
31367             },
31368             {
31369                 tag : 'div',
31370                 cls : 'btn-group roo-upload-cropbox-trash',
31371                 action : 'trash',
31372                 cn : [
31373                     {
31374                         tag : 'button',
31375                         cls : 'btn btn-default',
31376                         html : '<i class="fa fa-trash"></i>'
31377                     }
31378                 ]
31379             },
31380             {
31381                 tag : 'div',
31382                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31383                 action : 'rotate-right',
31384                 cn : [
31385                     {
31386                         tag : 'button',
31387                         cls : 'btn btn-default',
31388                         html : '<i class="fa fa-repeat"></i>'
31389                     }
31390                 ]
31391             }
31392         ],
31393         ROTATOR : [
31394             {
31395                 tag : 'div',
31396                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31397                 action : 'rotate-left',
31398                 cn : [
31399                     {
31400                         tag : 'button',
31401                         cls : 'btn btn-default',
31402                         html : '<i class="fa fa-undo"></i>'
31403                     }
31404                 ]
31405             },
31406             {
31407                 tag : 'div',
31408                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31409                 action : 'rotate-right',
31410                 cn : [
31411                     {
31412                         tag : 'button',
31413                         cls : 'btn btn-default',
31414                         html : '<i class="fa fa-repeat"></i>'
31415                     }
31416                 ]
31417             }
31418         ]
31419     }
31420 });
31421
31422 /*
31423 * Licence: LGPL
31424 */
31425
31426 /**
31427  * @class Roo.bootstrap.DocumentManager
31428  * @extends Roo.bootstrap.Component
31429  * Bootstrap DocumentManager class
31430  * @cfg {String} paramName default 'imageUpload'
31431  * @cfg {String} toolTipName default 'filename'
31432  * @cfg {String} method default POST
31433  * @cfg {String} url action url
31434  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31435  * @cfg {Boolean} multiple multiple upload default true
31436  * @cfg {Number} thumbSize default 300
31437  * @cfg {String} fieldLabel
31438  * @cfg {Number} labelWidth default 4
31439  * @cfg {String} labelAlign (left|top) default left
31440  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31441 * @cfg {Number} labellg set the width of label (1-12)
31442  * @cfg {Number} labelmd set the width of label (1-12)
31443  * @cfg {Number} labelsm set the width of label (1-12)
31444  * @cfg {Number} labelxs set the width of label (1-12)
31445  * 
31446  * @constructor
31447  * Create a new DocumentManager
31448  * @param {Object} config The config object
31449  */
31450
31451 Roo.bootstrap.DocumentManager = function(config){
31452     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31453     
31454     this.files = [];
31455     this.delegates = [];
31456     
31457     this.addEvents({
31458         /**
31459          * @event initial
31460          * Fire when initial the DocumentManager
31461          * @param {Roo.bootstrap.DocumentManager} this
31462          */
31463         "initial" : true,
31464         /**
31465          * @event inspect
31466          * inspect selected file
31467          * @param {Roo.bootstrap.DocumentManager} this
31468          * @param {File} file
31469          */
31470         "inspect" : true,
31471         /**
31472          * @event exception
31473          * Fire when xhr load exception
31474          * @param {Roo.bootstrap.DocumentManager} this
31475          * @param {XMLHttpRequest} xhr
31476          */
31477         "exception" : true,
31478         /**
31479          * @event afterupload
31480          * Fire when xhr load exception
31481          * @param {Roo.bootstrap.DocumentManager} this
31482          * @param {XMLHttpRequest} xhr
31483          */
31484         "afterupload" : true,
31485         /**
31486          * @event prepare
31487          * prepare the form data
31488          * @param {Roo.bootstrap.DocumentManager} this
31489          * @param {Object} formData
31490          */
31491         "prepare" : true,
31492         /**
31493          * @event remove
31494          * Fire when remove the file
31495          * @param {Roo.bootstrap.DocumentManager} this
31496          * @param {Object} file
31497          */
31498         "remove" : true,
31499         /**
31500          * @event refresh
31501          * Fire after refresh the file
31502          * @param {Roo.bootstrap.DocumentManager} this
31503          */
31504         "refresh" : true,
31505         /**
31506          * @event click
31507          * Fire after click the image
31508          * @param {Roo.bootstrap.DocumentManager} this
31509          * @param {Object} file
31510          */
31511         "click" : true,
31512         /**
31513          * @event edit
31514          * Fire when upload a image and editable set to true
31515          * @param {Roo.bootstrap.DocumentManager} this
31516          * @param {Object} file
31517          */
31518         "edit" : true,
31519         /**
31520          * @event beforeselectfile
31521          * Fire before select file
31522          * @param {Roo.bootstrap.DocumentManager} this
31523          */
31524         "beforeselectfile" : true,
31525         /**
31526          * @event process
31527          * Fire before process file
31528          * @param {Roo.bootstrap.DocumentManager} this
31529          * @param {Object} file
31530          */
31531         "process" : true,
31532         /**
31533          * @event previewrendered
31534          * Fire when preview rendered
31535          * @param {Roo.bootstrap.DocumentManager} this
31536          * @param {Object} file
31537          */
31538         "previewrendered" : true,
31539         /**
31540          */
31541         "previewResize" : true
31542         
31543     });
31544 };
31545
31546 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31547     
31548     boxes : 0,
31549     inputName : '',
31550     thumbSize : 300,
31551     multiple : true,
31552     files : false,
31553     method : 'POST',
31554     url : '',
31555     paramName : 'imageUpload',
31556     toolTipName : 'filename',
31557     fieldLabel : '',
31558     labelWidth : 4,
31559     labelAlign : 'left',
31560     editable : true,
31561     delegates : false,
31562     xhr : false, 
31563     
31564     labellg : 0,
31565     labelmd : 0,
31566     labelsm : 0,
31567     labelxs : 0,
31568     
31569     getAutoCreate : function()
31570     {   
31571         var managerWidget = {
31572             tag : 'div',
31573             cls : 'roo-document-manager',
31574             cn : [
31575                 {
31576                     tag : 'input',
31577                     cls : 'roo-document-manager-selector',
31578                     type : 'file'
31579                 },
31580                 {
31581                     tag : 'div',
31582                     cls : 'roo-document-manager-uploader',
31583                     cn : [
31584                         {
31585                             tag : 'div',
31586                             cls : 'roo-document-manager-upload-btn',
31587                             html : '<i class="fa fa-plus"></i>'
31588                         }
31589                     ]
31590                     
31591                 }
31592             ]
31593         };
31594         
31595         var content = [
31596             {
31597                 tag : 'div',
31598                 cls : 'column col-md-12',
31599                 cn : managerWidget
31600             }
31601         ];
31602         
31603         if(this.fieldLabel.length){
31604             
31605             content = [
31606                 {
31607                     tag : 'div',
31608                     cls : 'column col-md-12',
31609                     html : this.fieldLabel
31610                 },
31611                 {
31612                     tag : 'div',
31613                     cls : 'column col-md-12',
31614                     cn : managerWidget
31615                 }
31616             ];
31617
31618             if(this.labelAlign == 'left'){
31619                 content = [
31620                     {
31621                         tag : 'div',
31622                         cls : 'column',
31623                         html : this.fieldLabel
31624                     },
31625                     {
31626                         tag : 'div',
31627                         cls : 'column',
31628                         cn : managerWidget
31629                     }
31630                 ];
31631                 
31632                 if(this.labelWidth > 12){
31633                     content[0].style = "width: " + this.labelWidth + 'px';
31634                 }
31635
31636                 if(this.labelWidth < 13 && this.labelmd == 0){
31637                     this.labelmd = this.labelWidth;
31638                 }
31639
31640                 if(this.labellg > 0){
31641                     content[0].cls += ' col-lg-' + this.labellg;
31642                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31643                 }
31644
31645                 if(this.labelmd > 0){
31646                     content[0].cls += ' col-md-' + this.labelmd;
31647                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31648                 }
31649
31650                 if(this.labelsm > 0){
31651                     content[0].cls += ' col-sm-' + this.labelsm;
31652                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31653                 }
31654
31655                 if(this.labelxs > 0){
31656                     content[0].cls += ' col-xs-' + this.labelxs;
31657                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31658                 }
31659                 
31660             }
31661         }
31662         
31663         var cfg = {
31664             tag : 'div',
31665             cls : 'row clearfix',
31666             cn : content
31667         };
31668         
31669         return cfg;
31670         
31671     },
31672     
31673     initEvents : function()
31674     {
31675         this.managerEl = this.el.select('.roo-document-manager', true).first();
31676         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31677         
31678         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31679         this.selectorEl.hide();
31680         
31681         if(this.multiple){
31682             this.selectorEl.attr('multiple', 'multiple');
31683         }
31684         
31685         this.selectorEl.on('change', this.onFileSelected, this);
31686         
31687         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31688         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31689         
31690         this.uploader.on('click', this.onUploaderClick, this);
31691         
31692         this.renderProgressDialog();
31693         
31694         var _this = this;
31695         
31696         window.addEventListener("resize", function() { _this.refresh(); } );
31697         
31698         this.fireEvent('initial', this);
31699     },
31700     
31701     renderProgressDialog : function()
31702     {
31703         var _this = this;
31704         
31705         this.progressDialog = new Roo.bootstrap.Modal({
31706             cls : 'roo-document-manager-progress-dialog',
31707             allow_close : false,
31708             animate : false,
31709             title : '',
31710             buttons : [
31711                 {
31712                     name  :'cancel',
31713                     weight : 'danger',
31714                     html : 'Cancel'
31715                 }
31716             ], 
31717             listeners : { 
31718                 btnclick : function() {
31719                     _this.uploadCancel();
31720                     this.hide();
31721                 }
31722             }
31723         });
31724          
31725         this.progressDialog.render(Roo.get(document.body));
31726          
31727         this.progress = new Roo.bootstrap.Progress({
31728             cls : 'roo-document-manager-progress',
31729             active : true,
31730             striped : true
31731         });
31732         
31733         this.progress.render(this.progressDialog.getChildContainer());
31734         
31735         this.progressBar = new Roo.bootstrap.ProgressBar({
31736             cls : 'roo-document-manager-progress-bar',
31737             aria_valuenow : 0,
31738             aria_valuemin : 0,
31739             aria_valuemax : 12,
31740             panel : 'success'
31741         });
31742         
31743         this.progressBar.render(this.progress.getChildContainer());
31744     },
31745     
31746     onUploaderClick : function(e)
31747     {
31748         e.preventDefault();
31749      
31750         if(this.fireEvent('beforeselectfile', this) != false){
31751             this.selectorEl.dom.click();
31752         }
31753         
31754     },
31755     
31756     onFileSelected : function(e)
31757     {
31758         e.preventDefault();
31759         
31760         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31761             return;
31762         }
31763         
31764         Roo.each(this.selectorEl.dom.files, function(file){
31765             if(this.fireEvent('inspect', this, file) != false){
31766                 this.files.push(file);
31767             }
31768         }, this);
31769         
31770         this.queue();
31771         
31772     },
31773     
31774     queue : function()
31775     {
31776         this.selectorEl.dom.value = '';
31777         
31778         if(!this.files || !this.files.length){
31779             return;
31780         }
31781         
31782         if(this.boxes > 0 && this.files.length > this.boxes){
31783             this.files = this.files.slice(0, this.boxes);
31784         }
31785         
31786         this.uploader.show();
31787         
31788         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31789             this.uploader.hide();
31790         }
31791         
31792         var _this = this;
31793         
31794         var files = [];
31795         
31796         var docs = [];
31797         
31798         Roo.each(this.files, function(file){
31799             
31800             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31801                 var f = this.renderPreview(file);
31802                 files.push(f);
31803                 return;
31804             }
31805             
31806             if(file.type.indexOf('image') != -1){
31807                 this.delegates.push(
31808                     (function(){
31809                         _this.process(file);
31810                     }).createDelegate(this)
31811                 );
31812         
31813                 return;
31814             }
31815             
31816             docs.push(
31817                 (function(){
31818                     _this.process(file);
31819                 }).createDelegate(this)
31820             );
31821             
31822         }, this);
31823         
31824         this.files = files;
31825         
31826         this.delegates = this.delegates.concat(docs);
31827         
31828         if(!this.delegates.length){
31829             this.refresh();
31830             return;
31831         }
31832         
31833         this.progressBar.aria_valuemax = this.delegates.length;
31834         
31835         this.arrange();
31836         
31837         return;
31838     },
31839     
31840     arrange : function()
31841     {
31842         if(!this.delegates.length){
31843             this.progressDialog.hide();
31844             this.refresh();
31845             return;
31846         }
31847         
31848         var delegate = this.delegates.shift();
31849         
31850         this.progressDialog.show();
31851         
31852         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31853         
31854         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31855         
31856         delegate();
31857     },
31858     
31859     refresh : function()
31860     {
31861         this.uploader.show();
31862         
31863         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31864             this.uploader.hide();
31865         }
31866         
31867         Roo.isTouch ? this.closable(false) : this.closable(true);
31868         
31869         this.fireEvent('refresh', this);
31870     },
31871     
31872     onRemove : function(e, el, o)
31873     {
31874         e.preventDefault();
31875         
31876         this.fireEvent('remove', this, o);
31877         
31878     },
31879     
31880     remove : function(o)
31881     {
31882         var files = [];
31883         
31884         Roo.each(this.files, function(file){
31885             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31886                 files.push(file);
31887                 return;
31888             }
31889
31890             o.target.remove();
31891
31892         }, this);
31893         
31894         this.files = files;
31895         
31896         this.refresh();
31897     },
31898     
31899     clear : function()
31900     {
31901         Roo.each(this.files, function(file){
31902             if(!file.target){
31903                 return;
31904             }
31905             
31906             file.target.remove();
31907
31908         }, this);
31909         
31910         this.files = [];
31911         
31912         this.refresh();
31913     },
31914     
31915     onClick : function(e, el, o)
31916     {
31917         e.preventDefault();
31918         
31919         this.fireEvent('click', this, o);
31920         
31921     },
31922     
31923     closable : function(closable)
31924     {
31925         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31926             
31927             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31928             
31929             if(closable){
31930                 el.show();
31931                 return;
31932             }
31933             
31934             el.hide();
31935             
31936         }, this);
31937     },
31938     
31939     xhrOnLoad : function(xhr)
31940     {
31941         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31942             el.remove();
31943         }, this);
31944         
31945         if (xhr.readyState !== 4) {
31946             this.arrange();
31947             this.fireEvent('exception', this, xhr);
31948             return;
31949         }
31950
31951         var response = Roo.decode(xhr.responseText);
31952         
31953         if(!response.success){
31954             this.arrange();
31955             this.fireEvent('exception', this, xhr);
31956             return;
31957         }
31958         
31959         var file = this.renderPreview(response.data);
31960         
31961         this.files.push(file);
31962         
31963         this.arrange();
31964         
31965         this.fireEvent('afterupload', this, xhr);
31966         
31967     },
31968     
31969     xhrOnError : function(xhr)
31970     {
31971         Roo.log('xhr on error');
31972         
31973         var response = Roo.decode(xhr.responseText);
31974           
31975         Roo.log(response);
31976         
31977         this.arrange();
31978     },
31979     
31980     process : function(file)
31981     {
31982         if(this.fireEvent('process', this, file) !== false){
31983             if(this.editable && file.type.indexOf('image') != -1){
31984                 this.fireEvent('edit', this, file);
31985                 return;
31986             }
31987
31988             this.uploadStart(file, false);
31989
31990             return;
31991         }
31992         
31993     },
31994     
31995     uploadStart : function(file, crop)
31996     {
31997         this.xhr = new XMLHttpRequest();
31998         
31999         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32000             this.arrange();
32001             return;
32002         }
32003         
32004         file.xhr = this.xhr;
32005             
32006         this.managerEl.createChild({
32007             tag : 'div',
32008             cls : 'roo-document-manager-loading',
32009             cn : [
32010                 {
32011                     tag : 'div',
32012                     tooltip : file.name,
32013                     cls : 'roo-document-manager-thumb',
32014                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32015                 }
32016             ]
32017
32018         });
32019
32020         this.xhr.open(this.method, this.url, true);
32021         
32022         var headers = {
32023             "Accept": "application/json",
32024             "Cache-Control": "no-cache",
32025             "X-Requested-With": "XMLHttpRequest"
32026         };
32027         
32028         for (var headerName in headers) {
32029             var headerValue = headers[headerName];
32030             if (headerValue) {
32031                 this.xhr.setRequestHeader(headerName, headerValue);
32032             }
32033         }
32034         
32035         var _this = this;
32036         
32037         this.xhr.onload = function()
32038         {
32039             _this.xhrOnLoad(_this.xhr);
32040         }
32041         
32042         this.xhr.onerror = function()
32043         {
32044             _this.xhrOnError(_this.xhr);
32045         }
32046         
32047         var formData = new FormData();
32048
32049         formData.append('returnHTML', 'NO');
32050         
32051         if(crop){
32052             formData.append('crop', crop);
32053         }
32054         
32055         formData.append(this.paramName, file, file.name);
32056         
32057         var options = {
32058             file : file, 
32059             manually : false
32060         };
32061         
32062         if(this.fireEvent('prepare', this, formData, options) != false){
32063             
32064             if(options.manually){
32065                 return;
32066             }
32067             
32068             this.xhr.send(formData);
32069             return;
32070         };
32071         
32072         this.uploadCancel();
32073     },
32074     
32075     uploadCancel : function()
32076     {
32077         if (this.xhr) {
32078             this.xhr.abort();
32079         }
32080         
32081         this.delegates = [];
32082         
32083         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32084             el.remove();
32085         }, this);
32086         
32087         this.arrange();
32088     },
32089     
32090     renderPreview : function(file)
32091     {
32092         if(typeof(file.target) != 'undefined' && file.target){
32093             return file;
32094         }
32095         
32096         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32097         
32098         var previewEl = this.managerEl.createChild({
32099             tag : 'div',
32100             cls : 'roo-document-manager-preview',
32101             cn : [
32102                 {
32103                     tag : 'div',
32104                     tooltip : file[this.toolTipName],
32105                     cls : 'roo-document-manager-thumb',
32106                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32107                 },
32108                 {
32109                     tag : 'button',
32110                     cls : 'close',
32111                     html : '<i class="fa fa-times-circle"></i>'
32112                 }
32113             ]
32114         });
32115
32116         var close = previewEl.select('button.close', true).first();
32117
32118         close.on('click', this.onRemove, this, file);
32119
32120         file.target = previewEl;
32121
32122         var image = previewEl.select('img', true).first();
32123         
32124         var _this = this;
32125         
32126         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32127         
32128         image.on('click', this.onClick, this, file);
32129         
32130         this.fireEvent('previewrendered', this, file);
32131         
32132         return file;
32133         
32134     },
32135     
32136     onPreviewLoad : function(file, image)
32137     {
32138         if(typeof(file.target) == 'undefined' || !file.target){
32139             return;
32140         }
32141         
32142         var width = image.dom.naturalWidth || image.dom.width;
32143         var height = image.dom.naturalHeight || image.dom.height;
32144         
32145         if(!this.previewResize) {
32146             return;
32147         }
32148         
32149         if(width > height){
32150             file.target.addClass('wide');
32151             return;
32152         }
32153         
32154         file.target.addClass('tall');
32155         return;
32156         
32157     },
32158     
32159     uploadFromSource : function(file, crop)
32160     {
32161         this.xhr = new XMLHttpRequest();
32162         
32163         this.managerEl.createChild({
32164             tag : 'div',
32165             cls : 'roo-document-manager-loading',
32166             cn : [
32167                 {
32168                     tag : 'div',
32169                     tooltip : file.name,
32170                     cls : 'roo-document-manager-thumb',
32171                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32172                 }
32173             ]
32174
32175         });
32176
32177         this.xhr.open(this.method, this.url, true);
32178         
32179         var headers = {
32180             "Accept": "application/json",
32181             "Cache-Control": "no-cache",
32182             "X-Requested-With": "XMLHttpRequest"
32183         };
32184         
32185         for (var headerName in headers) {
32186             var headerValue = headers[headerName];
32187             if (headerValue) {
32188                 this.xhr.setRequestHeader(headerName, headerValue);
32189             }
32190         }
32191         
32192         var _this = this;
32193         
32194         this.xhr.onload = function()
32195         {
32196             _this.xhrOnLoad(_this.xhr);
32197         }
32198         
32199         this.xhr.onerror = function()
32200         {
32201             _this.xhrOnError(_this.xhr);
32202         }
32203         
32204         var formData = new FormData();
32205
32206         formData.append('returnHTML', 'NO');
32207         
32208         formData.append('crop', crop);
32209         
32210         if(typeof(file.filename) != 'undefined'){
32211             formData.append('filename', file.filename);
32212         }
32213         
32214         if(typeof(file.mimetype) != 'undefined'){
32215             formData.append('mimetype', file.mimetype);
32216         }
32217         
32218         Roo.log(formData);
32219         
32220         if(this.fireEvent('prepare', this, formData) != false){
32221             this.xhr.send(formData);
32222         };
32223     }
32224 });
32225
32226 /*
32227 * Licence: LGPL
32228 */
32229
32230 /**
32231  * @class Roo.bootstrap.DocumentViewer
32232  * @extends Roo.bootstrap.Component
32233  * Bootstrap DocumentViewer class
32234  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32235  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32236  * 
32237  * @constructor
32238  * Create a new DocumentViewer
32239  * @param {Object} config The config object
32240  */
32241
32242 Roo.bootstrap.DocumentViewer = function(config){
32243     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32244     
32245     this.addEvents({
32246         /**
32247          * @event initial
32248          * Fire after initEvent
32249          * @param {Roo.bootstrap.DocumentViewer} this
32250          */
32251         "initial" : true,
32252         /**
32253          * @event click
32254          * Fire after click
32255          * @param {Roo.bootstrap.DocumentViewer} this
32256          */
32257         "click" : true,
32258         /**
32259          * @event download
32260          * Fire after download button
32261          * @param {Roo.bootstrap.DocumentViewer} this
32262          */
32263         "download" : true,
32264         /**
32265          * @event trash
32266          * Fire after trash button
32267          * @param {Roo.bootstrap.DocumentViewer} this
32268          */
32269         "trash" : true
32270         
32271     });
32272 };
32273
32274 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32275     
32276     showDownload : true,
32277     
32278     showTrash : true,
32279     
32280     getAutoCreate : function()
32281     {
32282         var cfg = {
32283             tag : 'div',
32284             cls : 'roo-document-viewer',
32285             cn : [
32286                 {
32287                     tag : 'div',
32288                     cls : 'roo-document-viewer-body',
32289                     cn : [
32290                         {
32291                             tag : 'div',
32292                             cls : 'roo-document-viewer-thumb',
32293                             cn : [
32294                                 {
32295                                     tag : 'img',
32296                                     cls : 'roo-document-viewer-image'
32297                                 }
32298                             ]
32299                         }
32300                     ]
32301                 },
32302                 {
32303                     tag : 'div',
32304                     cls : 'roo-document-viewer-footer',
32305                     cn : {
32306                         tag : 'div',
32307                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32308                         cn : [
32309                             {
32310                                 tag : 'div',
32311                                 cls : 'btn-group roo-document-viewer-download',
32312                                 cn : [
32313                                     {
32314                                         tag : 'button',
32315                                         cls : 'btn btn-default',
32316                                         html : '<i class="fa fa-download"></i>'
32317                                     }
32318                                 ]
32319                             },
32320                             {
32321                                 tag : 'div',
32322                                 cls : 'btn-group roo-document-viewer-trash',
32323                                 cn : [
32324                                     {
32325                                         tag : 'button',
32326                                         cls : 'btn btn-default',
32327                                         html : '<i class="fa fa-trash"></i>'
32328                                     }
32329                                 ]
32330                             }
32331                         ]
32332                     }
32333                 }
32334             ]
32335         };
32336         
32337         return cfg;
32338     },
32339     
32340     initEvents : function()
32341     {
32342         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32343         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32344         
32345         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32346         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32347         
32348         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32349         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32350         
32351         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32352         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32353         
32354         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32355         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32356         
32357         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32358         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32359         
32360         this.bodyEl.on('click', this.onClick, this);
32361         this.downloadBtn.on('click', this.onDownload, this);
32362         this.trashBtn.on('click', this.onTrash, this);
32363         
32364         this.downloadBtn.hide();
32365         this.trashBtn.hide();
32366         
32367         if(this.showDownload){
32368             this.downloadBtn.show();
32369         }
32370         
32371         if(this.showTrash){
32372             this.trashBtn.show();
32373         }
32374         
32375         if(!this.showDownload && !this.showTrash) {
32376             this.footerEl.hide();
32377         }
32378         
32379     },
32380     
32381     initial : function()
32382     {
32383         this.fireEvent('initial', this);
32384         
32385     },
32386     
32387     onClick : function(e)
32388     {
32389         e.preventDefault();
32390         
32391         this.fireEvent('click', this);
32392     },
32393     
32394     onDownload : function(e)
32395     {
32396         e.preventDefault();
32397         
32398         this.fireEvent('download', this);
32399     },
32400     
32401     onTrash : function(e)
32402     {
32403         e.preventDefault();
32404         
32405         this.fireEvent('trash', this);
32406     }
32407     
32408 });
32409 /*
32410  * - LGPL
32411  *
32412  * nav progress bar
32413  * 
32414  */
32415
32416 /**
32417  * @class Roo.bootstrap.NavProgressBar
32418  * @extends Roo.bootstrap.Component
32419  * Bootstrap NavProgressBar class
32420  * 
32421  * @constructor
32422  * Create a new nav progress bar
32423  * @param {Object} config The config object
32424  */
32425
32426 Roo.bootstrap.NavProgressBar = function(config){
32427     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32428
32429     this.bullets = this.bullets || [];
32430    
32431 //    Roo.bootstrap.NavProgressBar.register(this);
32432      this.addEvents({
32433         /**
32434              * @event changed
32435              * Fires when the active item changes
32436              * @param {Roo.bootstrap.NavProgressBar} this
32437              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32438              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32439          */
32440         'changed': true
32441      });
32442     
32443 };
32444
32445 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32446     
32447     bullets : [],
32448     barItems : [],
32449     
32450     getAutoCreate : function()
32451     {
32452         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32453         
32454         cfg = {
32455             tag : 'div',
32456             cls : 'roo-navigation-bar-group',
32457             cn : [
32458                 {
32459                     tag : 'div',
32460                     cls : 'roo-navigation-top-bar'
32461                 },
32462                 {
32463                     tag : 'div',
32464                     cls : 'roo-navigation-bullets-bar',
32465                     cn : [
32466                         {
32467                             tag : 'ul',
32468                             cls : 'roo-navigation-bar'
32469                         }
32470                     ]
32471                 },
32472                 
32473                 {
32474                     tag : 'div',
32475                     cls : 'roo-navigation-bottom-bar'
32476                 }
32477             ]
32478             
32479         };
32480         
32481         return cfg;
32482         
32483     },
32484     
32485     initEvents: function() 
32486     {
32487         
32488     },
32489     
32490     onRender : function(ct, position) 
32491     {
32492         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32493         
32494         if(this.bullets.length){
32495             Roo.each(this.bullets, function(b){
32496                this.addItem(b);
32497             }, this);
32498         }
32499         
32500         this.format();
32501         
32502     },
32503     
32504     addItem : function(cfg)
32505     {
32506         var item = new Roo.bootstrap.NavProgressItem(cfg);
32507         
32508         item.parentId = this.id;
32509         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32510         
32511         if(cfg.html){
32512             var top = new Roo.bootstrap.Element({
32513                 tag : 'div',
32514                 cls : 'roo-navigation-bar-text'
32515             });
32516             
32517             var bottom = new Roo.bootstrap.Element({
32518                 tag : 'div',
32519                 cls : 'roo-navigation-bar-text'
32520             });
32521             
32522             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32523             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32524             
32525             var topText = new Roo.bootstrap.Element({
32526                 tag : 'span',
32527                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32528             });
32529             
32530             var bottomText = new Roo.bootstrap.Element({
32531                 tag : 'span',
32532                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32533             });
32534             
32535             topText.onRender(top.el, null);
32536             bottomText.onRender(bottom.el, null);
32537             
32538             item.topEl = top;
32539             item.bottomEl = bottom;
32540         }
32541         
32542         this.barItems.push(item);
32543         
32544         return item;
32545     },
32546     
32547     getActive : function()
32548     {
32549         var active = false;
32550         
32551         Roo.each(this.barItems, function(v){
32552             
32553             if (!v.isActive()) {
32554                 return;
32555             }
32556             
32557             active = v;
32558             return false;
32559             
32560         });
32561         
32562         return active;
32563     },
32564     
32565     setActiveItem : function(item)
32566     {
32567         var prev = false;
32568         
32569         Roo.each(this.barItems, function(v){
32570             if (v.rid == item.rid) {
32571                 return ;
32572             }
32573             
32574             if (v.isActive()) {
32575                 v.setActive(false);
32576                 prev = v;
32577             }
32578         });
32579
32580         item.setActive(true);
32581         
32582         this.fireEvent('changed', this, item, prev);
32583     },
32584     
32585     getBarItem: function(rid)
32586     {
32587         var ret = false;
32588         
32589         Roo.each(this.barItems, function(e) {
32590             if (e.rid != rid) {
32591                 return;
32592             }
32593             
32594             ret =  e;
32595             return false;
32596         });
32597         
32598         return ret;
32599     },
32600     
32601     indexOfItem : function(item)
32602     {
32603         var index = false;
32604         
32605         Roo.each(this.barItems, function(v, i){
32606             
32607             if (v.rid != item.rid) {
32608                 return;
32609             }
32610             
32611             index = i;
32612             return false
32613         });
32614         
32615         return index;
32616     },
32617     
32618     setActiveNext : function()
32619     {
32620         var i = this.indexOfItem(this.getActive());
32621         
32622         if (i > this.barItems.length) {
32623             return;
32624         }
32625         
32626         this.setActiveItem(this.barItems[i+1]);
32627     },
32628     
32629     setActivePrev : function()
32630     {
32631         var i = this.indexOfItem(this.getActive());
32632         
32633         if (i  < 1) {
32634             return;
32635         }
32636         
32637         this.setActiveItem(this.barItems[i-1]);
32638     },
32639     
32640     format : function()
32641     {
32642         if(!this.barItems.length){
32643             return;
32644         }
32645      
32646         var width = 100 / this.barItems.length;
32647         
32648         Roo.each(this.barItems, function(i){
32649             i.el.setStyle('width', width + '%');
32650             i.topEl.el.setStyle('width', width + '%');
32651             i.bottomEl.el.setStyle('width', width + '%');
32652         }, this);
32653         
32654     }
32655     
32656 });
32657 /*
32658  * - LGPL
32659  *
32660  * Nav Progress Item
32661  * 
32662  */
32663
32664 /**
32665  * @class Roo.bootstrap.NavProgressItem
32666  * @extends Roo.bootstrap.Component
32667  * Bootstrap NavProgressItem class
32668  * @cfg {String} rid the reference id
32669  * @cfg {Boolean} active (true|false) Is item active default false
32670  * @cfg {Boolean} disabled (true|false) Is item active default false
32671  * @cfg {String} html
32672  * @cfg {String} position (top|bottom) text position default bottom
32673  * @cfg {String} icon show icon instead of number
32674  * 
32675  * @constructor
32676  * Create a new NavProgressItem
32677  * @param {Object} config The config object
32678  */
32679 Roo.bootstrap.NavProgressItem = function(config){
32680     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32681     this.addEvents({
32682         // raw events
32683         /**
32684          * @event click
32685          * The raw click event for the entire grid.
32686          * @param {Roo.bootstrap.NavProgressItem} this
32687          * @param {Roo.EventObject} e
32688          */
32689         "click" : true
32690     });
32691    
32692 };
32693
32694 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32695     
32696     rid : '',
32697     active : false,
32698     disabled : false,
32699     html : '',
32700     position : 'bottom',
32701     icon : false,
32702     
32703     getAutoCreate : function()
32704     {
32705         var iconCls = 'roo-navigation-bar-item-icon';
32706         
32707         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32708         
32709         var cfg = {
32710             tag: 'li',
32711             cls: 'roo-navigation-bar-item',
32712             cn : [
32713                 {
32714                     tag : 'i',
32715                     cls : iconCls
32716                 }
32717             ]
32718         };
32719         
32720         if(this.active){
32721             cfg.cls += ' active';
32722         }
32723         if(this.disabled){
32724             cfg.cls += ' disabled';
32725         }
32726         
32727         return cfg;
32728     },
32729     
32730     disable : function()
32731     {
32732         this.setDisabled(true);
32733     },
32734     
32735     enable : function()
32736     {
32737         this.setDisabled(false);
32738     },
32739     
32740     initEvents: function() 
32741     {
32742         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32743         
32744         this.iconEl.on('click', this.onClick, this);
32745     },
32746     
32747     onClick : function(e)
32748     {
32749         e.preventDefault();
32750         
32751         if(this.disabled){
32752             return;
32753         }
32754         
32755         if(this.fireEvent('click', this, e) === false){
32756             return;
32757         };
32758         
32759         this.parent().setActiveItem(this);
32760     },
32761     
32762     isActive: function () 
32763     {
32764         return this.active;
32765     },
32766     
32767     setActive : function(state)
32768     {
32769         if(this.active == state){
32770             return;
32771         }
32772         
32773         this.active = state;
32774         
32775         if (state) {
32776             this.el.addClass('active');
32777             return;
32778         }
32779         
32780         this.el.removeClass('active');
32781         
32782         return;
32783     },
32784     
32785     setDisabled : function(state)
32786     {
32787         if(this.disabled == state){
32788             return;
32789         }
32790         
32791         this.disabled = state;
32792         
32793         if (state) {
32794             this.el.addClass('disabled');
32795             return;
32796         }
32797         
32798         this.el.removeClass('disabled');
32799     },
32800     
32801     tooltipEl : function()
32802     {
32803         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32804     }
32805 });
32806  
32807
32808  /*
32809  * - LGPL
32810  *
32811  * FieldLabel
32812  * 
32813  */
32814
32815 /**
32816  * @class Roo.bootstrap.FieldLabel
32817  * @extends Roo.bootstrap.Component
32818  * Bootstrap FieldLabel class
32819  * @cfg {String} html contents of the element
32820  * @cfg {String} tag tag of the element default label
32821  * @cfg {String} cls class of the element
32822  * @cfg {String} target label target 
32823  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32824  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32825  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32826  * @cfg {String} iconTooltip default "This field is required"
32827  * @cfg {String} indicatorpos (left|right) default left
32828  * 
32829  * @constructor
32830  * Create a new FieldLabel
32831  * @param {Object} config The config object
32832  */
32833
32834 Roo.bootstrap.FieldLabel = function(config){
32835     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32836     
32837     this.addEvents({
32838             /**
32839              * @event invalid
32840              * Fires after the field has been marked as invalid.
32841              * @param {Roo.form.FieldLabel} this
32842              * @param {String} msg The validation message
32843              */
32844             invalid : true,
32845             /**
32846              * @event valid
32847              * Fires after the field has been validated with no errors.
32848              * @param {Roo.form.FieldLabel} this
32849              */
32850             valid : true
32851         });
32852 };
32853
32854 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32855     
32856     tag: 'label',
32857     cls: '',
32858     html: '',
32859     target: '',
32860     allowBlank : true,
32861     invalidClass : 'has-warning',
32862     validClass : 'has-success',
32863     iconTooltip : 'This field is required',
32864     indicatorpos : 'left',
32865     
32866     getAutoCreate : function(){
32867         
32868         var cls = "";
32869         if (!this.allowBlank) {
32870             cls  = "visible";
32871         }
32872         
32873         var cfg = {
32874             tag : this.tag,
32875             cls : 'roo-bootstrap-field-label ' + this.cls,
32876             for : this.target,
32877             cn : [
32878                 {
32879                     tag : 'i',
32880                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32881                     tooltip : this.iconTooltip
32882                 },
32883                 {
32884                     tag : 'span',
32885                     html : this.html
32886                 }
32887             ] 
32888         };
32889         
32890         if(this.indicatorpos == 'right'){
32891             var cfg = {
32892                 tag : this.tag,
32893                 cls : 'roo-bootstrap-field-label ' + this.cls,
32894                 for : this.target,
32895                 cn : [
32896                     {
32897                         tag : 'span',
32898                         html : this.html
32899                     },
32900                     {
32901                         tag : 'i',
32902                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32903                         tooltip : this.iconTooltip
32904                     }
32905                 ] 
32906             };
32907         }
32908         
32909         return cfg;
32910     },
32911     
32912     initEvents: function() 
32913     {
32914         Roo.bootstrap.Element.superclass.initEvents.call(this);
32915         
32916         this.indicator = this.indicatorEl();
32917         
32918         if(this.indicator){
32919             this.indicator.removeClass('visible');
32920             this.indicator.addClass('invisible');
32921         }
32922         
32923         Roo.bootstrap.FieldLabel.register(this);
32924     },
32925     
32926     indicatorEl : function()
32927     {
32928         var indicator = this.el.select('i.roo-required-indicator',true).first();
32929         
32930         if(!indicator){
32931             return false;
32932         }
32933         
32934         return indicator;
32935         
32936     },
32937     
32938     /**
32939      * Mark this field as valid
32940      */
32941     markValid : function()
32942     {
32943         if(this.indicator){
32944             this.indicator.removeClass('visible');
32945             this.indicator.addClass('invisible');
32946         }
32947         if (Roo.bootstrap.version == 3) {
32948             this.el.removeClass(this.invalidClass);
32949             this.el.addClass(this.validClass);
32950         } else {
32951             this.el.removeClass('is-invalid');
32952             this.el.addClass('is-valid');
32953         }
32954         
32955         
32956         this.fireEvent('valid', this);
32957     },
32958     
32959     /**
32960      * Mark this field as invalid
32961      * @param {String} msg The validation message
32962      */
32963     markInvalid : function(msg)
32964     {
32965         if(this.indicator){
32966             this.indicator.removeClass('invisible');
32967             this.indicator.addClass('visible');
32968         }
32969           if (Roo.bootstrap.version == 3) {
32970             this.el.removeClass(this.validClass);
32971             this.el.addClass(this.invalidClass);
32972         } else {
32973             this.el.removeClass('is-valid');
32974             this.el.addClass('is-invalid');
32975         }
32976         
32977         
32978         this.fireEvent('invalid', this, msg);
32979     }
32980     
32981    
32982 });
32983
32984 Roo.apply(Roo.bootstrap.FieldLabel, {
32985     
32986     groups: {},
32987     
32988      /**
32989     * register a FieldLabel Group
32990     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32991     */
32992     register : function(label)
32993     {
32994         if(this.groups.hasOwnProperty(label.target)){
32995             return;
32996         }
32997      
32998         this.groups[label.target] = label;
32999         
33000     },
33001     /**
33002     * fetch a FieldLabel Group based on the target
33003     * @param {string} target
33004     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33005     */
33006     get: function(target) {
33007         if (typeof(this.groups[target]) == 'undefined') {
33008             return false;
33009         }
33010         
33011         return this.groups[target] ;
33012     }
33013 });
33014
33015  
33016
33017  /*
33018  * - LGPL
33019  *
33020  * page DateSplitField.
33021  * 
33022  */
33023
33024
33025 /**
33026  * @class Roo.bootstrap.DateSplitField
33027  * @extends Roo.bootstrap.Component
33028  * Bootstrap DateSplitField class
33029  * @cfg {string} fieldLabel - the label associated
33030  * @cfg {Number} labelWidth set the width of label (0-12)
33031  * @cfg {String} labelAlign (top|left)
33032  * @cfg {Boolean} dayAllowBlank (true|false) default false
33033  * @cfg {Boolean} monthAllowBlank (true|false) default false
33034  * @cfg {Boolean} yearAllowBlank (true|false) default false
33035  * @cfg {string} dayPlaceholder 
33036  * @cfg {string} monthPlaceholder
33037  * @cfg {string} yearPlaceholder
33038  * @cfg {string} dayFormat default 'd'
33039  * @cfg {string} monthFormat default 'm'
33040  * @cfg {string} yearFormat default 'Y'
33041  * @cfg {Number} labellg set the width of label (1-12)
33042  * @cfg {Number} labelmd set the width of label (1-12)
33043  * @cfg {Number} labelsm set the width of label (1-12)
33044  * @cfg {Number} labelxs set the width of label (1-12)
33045
33046  *     
33047  * @constructor
33048  * Create a new DateSplitField
33049  * @param {Object} config The config object
33050  */
33051
33052 Roo.bootstrap.DateSplitField = function(config){
33053     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33054     
33055     this.addEvents({
33056         // raw events
33057          /**
33058          * @event years
33059          * getting the data of years
33060          * @param {Roo.bootstrap.DateSplitField} this
33061          * @param {Object} years
33062          */
33063         "years" : true,
33064         /**
33065          * @event days
33066          * getting the data of days
33067          * @param {Roo.bootstrap.DateSplitField} this
33068          * @param {Object} days
33069          */
33070         "days" : true,
33071         /**
33072          * @event invalid
33073          * Fires after the field has been marked as invalid.
33074          * @param {Roo.form.Field} this
33075          * @param {String} msg The validation message
33076          */
33077         invalid : true,
33078        /**
33079          * @event valid
33080          * Fires after the field has been validated with no errors.
33081          * @param {Roo.form.Field} this
33082          */
33083         valid : true
33084     });
33085 };
33086
33087 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33088     
33089     fieldLabel : '',
33090     labelAlign : 'top',
33091     labelWidth : 3,
33092     dayAllowBlank : false,
33093     monthAllowBlank : false,
33094     yearAllowBlank : false,
33095     dayPlaceholder : '',
33096     monthPlaceholder : '',
33097     yearPlaceholder : '',
33098     dayFormat : 'd',
33099     monthFormat : 'm',
33100     yearFormat : 'Y',
33101     isFormField : true,
33102     labellg : 0,
33103     labelmd : 0,
33104     labelsm : 0,
33105     labelxs : 0,
33106     
33107     getAutoCreate : function()
33108     {
33109         var cfg = {
33110             tag : 'div',
33111             cls : 'row roo-date-split-field-group',
33112             cn : [
33113                 {
33114                     tag : 'input',
33115                     type : 'hidden',
33116                     cls : 'form-hidden-field roo-date-split-field-group-value',
33117                     name : this.name
33118                 }
33119             ]
33120         };
33121         
33122         var labelCls = 'col-md-12';
33123         var contentCls = 'col-md-4';
33124         
33125         if(this.fieldLabel){
33126             
33127             var label = {
33128                 tag : 'div',
33129                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33130                 cn : [
33131                     {
33132                         tag : 'label',
33133                         html : this.fieldLabel
33134                     }
33135                 ]
33136             };
33137             
33138             if(this.labelAlign == 'left'){
33139             
33140                 if(this.labelWidth > 12){
33141                     label.style = "width: " + this.labelWidth + 'px';
33142                 }
33143
33144                 if(this.labelWidth < 13 && this.labelmd == 0){
33145                     this.labelmd = this.labelWidth;
33146                 }
33147
33148                 if(this.labellg > 0){
33149                     labelCls = ' col-lg-' + this.labellg;
33150                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33151                 }
33152
33153                 if(this.labelmd > 0){
33154                     labelCls = ' col-md-' + this.labelmd;
33155                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33156                 }
33157
33158                 if(this.labelsm > 0){
33159                     labelCls = ' col-sm-' + this.labelsm;
33160                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33161                 }
33162
33163                 if(this.labelxs > 0){
33164                     labelCls = ' col-xs-' + this.labelxs;
33165                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33166                 }
33167             }
33168             
33169             label.cls += ' ' + labelCls;
33170             
33171             cfg.cn.push(label);
33172         }
33173         
33174         Roo.each(['day', 'month', 'year'], function(t){
33175             cfg.cn.push({
33176                 tag : 'div',
33177                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33178             });
33179         }, this);
33180         
33181         return cfg;
33182     },
33183     
33184     inputEl: function ()
33185     {
33186         return this.el.select('.roo-date-split-field-group-value', true).first();
33187     },
33188     
33189     onRender : function(ct, position) 
33190     {
33191         var _this = this;
33192         
33193         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33194         
33195         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33196         
33197         this.dayField = new Roo.bootstrap.ComboBox({
33198             allowBlank : this.dayAllowBlank,
33199             alwaysQuery : true,
33200             displayField : 'value',
33201             editable : false,
33202             fieldLabel : '',
33203             forceSelection : true,
33204             mode : 'local',
33205             placeholder : this.dayPlaceholder,
33206             selectOnFocus : true,
33207             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33208             triggerAction : 'all',
33209             typeAhead : true,
33210             valueField : 'value',
33211             store : new Roo.data.SimpleStore({
33212                 data : (function() {    
33213                     var days = [];
33214                     _this.fireEvent('days', _this, days);
33215                     return days;
33216                 })(),
33217                 fields : [ 'value' ]
33218             }),
33219             listeners : {
33220                 select : function (_self, record, index)
33221                 {
33222                     _this.setValue(_this.getValue());
33223                 }
33224             }
33225         });
33226
33227         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33228         
33229         this.monthField = new Roo.bootstrap.MonthField({
33230             after : '<i class=\"fa fa-calendar\"></i>',
33231             allowBlank : this.monthAllowBlank,
33232             placeholder : this.monthPlaceholder,
33233             readOnly : true,
33234             listeners : {
33235                 render : function (_self)
33236                 {
33237                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33238                         e.preventDefault();
33239                         _self.focus();
33240                     });
33241                 },
33242                 select : function (_self, oldvalue, newvalue)
33243                 {
33244                     _this.setValue(_this.getValue());
33245                 }
33246             }
33247         });
33248         
33249         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33250         
33251         this.yearField = new Roo.bootstrap.ComboBox({
33252             allowBlank : this.yearAllowBlank,
33253             alwaysQuery : true,
33254             displayField : 'value',
33255             editable : false,
33256             fieldLabel : '',
33257             forceSelection : true,
33258             mode : 'local',
33259             placeholder : this.yearPlaceholder,
33260             selectOnFocus : true,
33261             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33262             triggerAction : 'all',
33263             typeAhead : true,
33264             valueField : 'value',
33265             store : new Roo.data.SimpleStore({
33266                 data : (function() {
33267                     var years = [];
33268                     _this.fireEvent('years', _this, years);
33269                     return years;
33270                 })(),
33271                 fields : [ 'value' ]
33272             }),
33273             listeners : {
33274                 select : function (_self, record, index)
33275                 {
33276                     _this.setValue(_this.getValue());
33277                 }
33278             }
33279         });
33280
33281         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33282     },
33283     
33284     setValue : function(v, format)
33285     {
33286         this.inputEl.dom.value = v;
33287         
33288         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33289         
33290         var d = Date.parseDate(v, f);
33291         
33292         if(!d){
33293             this.validate();
33294             return;
33295         }
33296         
33297         this.setDay(d.format(this.dayFormat));
33298         this.setMonth(d.format(this.monthFormat));
33299         this.setYear(d.format(this.yearFormat));
33300         
33301         this.validate();
33302         
33303         return;
33304     },
33305     
33306     setDay : function(v)
33307     {
33308         this.dayField.setValue(v);
33309         this.inputEl.dom.value = this.getValue();
33310         this.validate();
33311         return;
33312     },
33313     
33314     setMonth : function(v)
33315     {
33316         this.monthField.setValue(v, true);
33317         this.inputEl.dom.value = this.getValue();
33318         this.validate();
33319         return;
33320     },
33321     
33322     setYear : function(v)
33323     {
33324         this.yearField.setValue(v);
33325         this.inputEl.dom.value = this.getValue();
33326         this.validate();
33327         return;
33328     },
33329     
33330     getDay : function()
33331     {
33332         return this.dayField.getValue();
33333     },
33334     
33335     getMonth : function()
33336     {
33337         return this.monthField.getValue();
33338     },
33339     
33340     getYear : function()
33341     {
33342         return this.yearField.getValue();
33343     },
33344     
33345     getValue : function()
33346     {
33347         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33348         
33349         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33350         
33351         return date;
33352     },
33353     
33354     reset : function()
33355     {
33356         this.setDay('');
33357         this.setMonth('');
33358         this.setYear('');
33359         this.inputEl.dom.value = '';
33360         this.validate();
33361         return;
33362     },
33363     
33364     validate : function()
33365     {
33366         var d = this.dayField.validate();
33367         var m = this.monthField.validate();
33368         var y = this.yearField.validate();
33369         
33370         var valid = true;
33371         
33372         if(
33373                 (!this.dayAllowBlank && !d) ||
33374                 (!this.monthAllowBlank && !m) ||
33375                 (!this.yearAllowBlank && !y)
33376         ){
33377             valid = false;
33378         }
33379         
33380         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33381             return valid;
33382         }
33383         
33384         if(valid){
33385             this.markValid();
33386             return valid;
33387         }
33388         
33389         this.markInvalid();
33390         
33391         return valid;
33392     },
33393     
33394     markValid : function()
33395     {
33396         
33397         var label = this.el.select('label', true).first();
33398         var icon = this.el.select('i.fa-star', true).first();
33399
33400         if(label && icon){
33401             icon.remove();
33402         }
33403         
33404         this.fireEvent('valid', this);
33405     },
33406     
33407      /**
33408      * Mark this field as invalid
33409      * @param {String} msg The validation message
33410      */
33411     markInvalid : function(msg)
33412     {
33413         
33414         var label = this.el.select('label', true).first();
33415         var icon = this.el.select('i.fa-star', true).first();
33416
33417         if(label && !icon){
33418             this.el.select('.roo-date-split-field-label', true).createChild({
33419                 tag : 'i',
33420                 cls : 'text-danger fa fa-lg fa-star',
33421                 tooltip : 'This field is required',
33422                 style : 'margin-right:5px;'
33423             }, label, true);
33424         }
33425         
33426         this.fireEvent('invalid', this, msg);
33427     },
33428     
33429     clearInvalid : function()
33430     {
33431         var label = this.el.select('label', true).first();
33432         var icon = this.el.select('i.fa-star', true).first();
33433
33434         if(label && icon){
33435             icon.remove();
33436         }
33437         
33438         this.fireEvent('valid', this);
33439     },
33440     
33441     getName: function()
33442     {
33443         return this.name;
33444     }
33445     
33446 });
33447
33448  /**
33449  *
33450  * This is based on 
33451  * http://masonry.desandro.com
33452  *
33453  * The idea is to render all the bricks based on vertical width...
33454  *
33455  * The original code extends 'outlayer' - we might need to use that....
33456  * 
33457  */
33458
33459
33460 /**
33461  * @class Roo.bootstrap.LayoutMasonry
33462  * @extends Roo.bootstrap.Component
33463  * Bootstrap Layout Masonry class
33464  * 
33465  * @constructor
33466  * Create a new Element
33467  * @param {Object} config The config object
33468  */
33469
33470 Roo.bootstrap.LayoutMasonry = function(config){
33471     
33472     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33473     
33474     this.bricks = [];
33475     
33476     Roo.bootstrap.LayoutMasonry.register(this);
33477     
33478     this.addEvents({
33479         // raw events
33480         /**
33481          * @event layout
33482          * Fire after layout the items
33483          * @param {Roo.bootstrap.LayoutMasonry} this
33484          * @param {Roo.EventObject} e
33485          */
33486         "layout" : true
33487     });
33488     
33489 };
33490
33491 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33492     
33493     /**
33494      * @cfg {Boolean} isLayoutInstant = no animation?
33495      */   
33496     isLayoutInstant : false, // needed?
33497    
33498     /**
33499      * @cfg {Number} boxWidth  width of the columns
33500      */   
33501     boxWidth : 450,
33502     
33503       /**
33504      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33505      */   
33506     boxHeight : 0,
33507     
33508     /**
33509      * @cfg {Number} padWidth padding below box..
33510      */   
33511     padWidth : 10, 
33512     
33513     /**
33514      * @cfg {Number} gutter gutter width..
33515      */   
33516     gutter : 10,
33517     
33518      /**
33519      * @cfg {Number} maxCols maximum number of columns
33520      */   
33521     
33522     maxCols: 0,
33523     
33524     /**
33525      * @cfg {Boolean} isAutoInitial defalut true
33526      */   
33527     isAutoInitial : true, 
33528     
33529     containerWidth: 0,
33530     
33531     /**
33532      * @cfg {Boolean} isHorizontal defalut false
33533      */   
33534     isHorizontal : false, 
33535
33536     currentSize : null,
33537     
33538     tag: 'div',
33539     
33540     cls: '',
33541     
33542     bricks: null, //CompositeElement
33543     
33544     cols : 1,
33545     
33546     _isLayoutInited : false,
33547     
33548 //    isAlternative : false, // only use for vertical layout...
33549     
33550     /**
33551      * @cfg {Number} alternativePadWidth padding below box..
33552      */   
33553     alternativePadWidth : 50,
33554     
33555     selectedBrick : [],
33556     
33557     getAutoCreate : function(){
33558         
33559         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33560         
33561         var cfg = {
33562             tag: this.tag,
33563             cls: 'blog-masonary-wrapper ' + this.cls,
33564             cn : {
33565                 cls : 'mas-boxes masonary'
33566             }
33567         };
33568         
33569         return cfg;
33570     },
33571     
33572     getChildContainer: function( )
33573     {
33574         if (this.boxesEl) {
33575             return this.boxesEl;
33576         }
33577         
33578         this.boxesEl = this.el.select('.mas-boxes').first();
33579         
33580         return this.boxesEl;
33581     },
33582     
33583     
33584     initEvents : function()
33585     {
33586         var _this = this;
33587         
33588         if(this.isAutoInitial){
33589             Roo.log('hook children rendered');
33590             this.on('childrenrendered', function() {
33591                 Roo.log('children rendered');
33592                 _this.initial();
33593             } ,this);
33594         }
33595     },
33596     
33597     initial : function()
33598     {
33599         this.selectedBrick = [];
33600         
33601         this.currentSize = this.el.getBox(true);
33602         
33603         Roo.EventManager.onWindowResize(this.resize, this); 
33604
33605         if(!this.isAutoInitial){
33606             this.layout();
33607             return;
33608         }
33609         
33610         this.layout();
33611         
33612         return;
33613         //this.layout.defer(500,this);
33614         
33615     },
33616     
33617     resize : function()
33618     {
33619         var cs = this.el.getBox(true);
33620         
33621         if (
33622                 this.currentSize.width == cs.width && 
33623                 this.currentSize.x == cs.x && 
33624                 this.currentSize.height == cs.height && 
33625                 this.currentSize.y == cs.y 
33626         ) {
33627             Roo.log("no change in with or X or Y");
33628             return;
33629         }
33630         
33631         this.currentSize = cs;
33632         
33633         this.layout();
33634         
33635     },
33636     
33637     layout : function()
33638     {   
33639         this._resetLayout();
33640         
33641         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33642         
33643         this.layoutItems( isInstant );
33644       
33645         this._isLayoutInited = true;
33646         
33647         this.fireEvent('layout', this);
33648         
33649     },
33650     
33651     _resetLayout : function()
33652     {
33653         if(this.isHorizontal){
33654             this.horizontalMeasureColumns();
33655             return;
33656         }
33657         
33658         this.verticalMeasureColumns();
33659         
33660     },
33661     
33662     verticalMeasureColumns : function()
33663     {
33664         this.getContainerWidth();
33665         
33666 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33667 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33668 //            return;
33669 //        }
33670         
33671         var boxWidth = this.boxWidth + this.padWidth;
33672         
33673         if(this.containerWidth < this.boxWidth){
33674             boxWidth = this.containerWidth
33675         }
33676         
33677         var containerWidth = this.containerWidth;
33678         
33679         var cols = Math.floor(containerWidth / boxWidth);
33680         
33681         this.cols = Math.max( cols, 1 );
33682         
33683         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33684         
33685         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33686         
33687         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33688         
33689         this.colWidth = boxWidth + avail - this.padWidth;
33690         
33691         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33692         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33693     },
33694     
33695     horizontalMeasureColumns : function()
33696     {
33697         this.getContainerWidth();
33698         
33699         var boxWidth = this.boxWidth;
33700         
33701         if(this.containerWidth < boxWidth){
33702             boxWidth = this.containerWidth;
33703         }
33704         
33705         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33706         
33707         this.el.setHeight(boxWidth);
33708         
33709     },
33710     
33711     getContainerWidth : function()
33712     {
33713         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33714     },
33715     
33716     layoutItems : function( isInstant )
33717     {
33718         Roo.log(this.bricks);
33719         
33720         var items = Roo.apply([], this.bricks);
33721         
33722         if(this.isHorizontal){
33723             this._horizontalLayoutItems( items , isInstant );
33724             return;
33725         }
33726         
33727 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33728 //            this._verticalAlternativeLayoutItems( items , isInstant );
33729 //            return;
33730 //        }
33731         
33732         this._verticalLayoutItems( items , isInstant );
33733         
33734     },
33735     
33736     _verticalLayoutItems : function ( items , isInstant)
33737     {
33738         if ( !items || !items.length ) {
33739             return;
33740         }
33741         
33742         var standard = [
33743             ['xs', 'xs', 'xs', 'tall'],
33744             ['xs', 'xs', 'tall'],
33745             ['xs', 'xs', 'sm'],
33746             ['xs', 'xs', 'xs'],
33747             ['xs', 'tall'],
33748             ['xs', 'sm'],
33749             ['xs', 'xs'],
33750             ['xs'],
33751             
33752             ['sm', 'xs', 'xs'],
33753             ['sm', 'xs'],
33754             ['sm'],
33755             
33756             ['tall', 'xs', 'xs', 'xs'],
33757             ['tall', 'xs', 'xs'],
33758             ['tall', 'xs'],
33759             ['tall']
33760             
33761         ];
33762         
33763         var queue = [];
33764         
33765         var boxes = [];
33766         
33767         var box = [];
33768         
33769         Roo.each(items, function(item, k){
33770             
33771             switch (item.size) {
33772                 // these layouts take up a full box,
33773                 case 'md' :
33774                 case 'md-left' :
33775                 case 'md-right' :
33776                 case 'wide' :
33777                     
33778                     if(box.length){
33779                         boxes.push(box);
33780                         box = [];
33781                     }
33782                     
33783                     boxes.push([item]);
33784                     
33785                     break;
33786                     
33787                 case 'xs' :
33788                 case 'sm' :
33789                 case 'tall' :
33790                     
33791                     box.push(item);
33792                     
33793                     break;
33794                 default :
33795                     break;
33796                     
33797             }
33798             
33799         }, this);
33800         
33801         if(box.length){
33802             boxes.push(box);
33803             box = [];
33804         }
33805         
33806         var filterPattern = function(box, length)
33807         {
33808             if(!box.length){
33809                 return;
33810             }
33811             
33812             var match = false;
33813             
33814             var pattern = box.slice(0, length);
33815             
33816             var format = [];
33817             
33818             Roo.each(pattern, function(i){
33819                 format.push(i.size);
33820             }, this);
33821             
33822             Roo.each(standard, function(s){
33823                 
33824                 if(String(s) != String(format)){
33825                     return;
33826                 }
33827                 
33828                 match = true;
33829                 return false;
33830                 
33831             }, this);
33832             
33833             if(!match && length == 1){
33834                 return;
33835             }
33836             
33837             if(!match){
33838                 filterPattern(box, length - 1);
33839                 return;
33840             }
33841                 
33842             queue.push(pattern);
33843
33844             box = box.slice(length, box.length);
33845
33846             filterPattern(box, 4);
33847
33848             return;
33849             
33850         }
33851         
33852         Roo.each(boxes, function(box, k){
33853             
33854             if(!box.length){
33855                 return;
33856             }
33857             
33858             if(box.length == 1){
33859                 queue.push(box);
33860                 return;
33861             }
33862             
33863             filterPattern(box, 4);
33864             
33865         }, this);
33866         
33867         this._processVerticalLayoutQueue( queue, isInstant );
33868         
33869     },
33870     
33871 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33872 //    {
33873 //        if ( !items || !items.length ) {
33874 //            return;
33875 //        }
33876 //
33877 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33878 //        
33879 //    },
33880     
33881     _horizontalLayoutItems : function ( items , isInstant)
33882     {
33883         if ( !items || !items.length || items.length < 3) {
33884             return;
33885         }
33886         
33887         items.reverse();
33888         
33889         var eItems = items.slice(0, 3);
33890         
33891         items = items.slice(3, items.length);
33892         
33893         var standard = [
33894             ['xs', 'xs', 'xs', 'wide'],
33895             ['xs', 'xs', 'wide'],
33896             ['xs', 'xs', 'sm'],
33897             ['xs', 'xs', 'xs'],
33898             ['xs', 'wide'],
33899             ['xs', 'sm'],
33900             ['xs', 'xs'],
33901             ['xs'],
33902             
33903             ['sm', 'xs', 'xs'],
33904             ['sm', 'xs'],
33905             ['sm'],
33906             
33907             ['wide', 'xs', 'xs', 'xs'],
33908             ['wide', 'xs', 'xs'],
33909             ['wide', 'xs'],
33910             ['wide'],
33911             
33912             ['wide-thin']
33913         ];
33914         
33915         var queue = [];
33916         
33917         var boxes = [];
33918         
33919         var box = [];
33920         
33921         Roo.each(items, function(item, k){
33922             
33923             switch (item.size) {
33924                 case 'md' :
33925                 case 'md-left' :
33926                 case 'md-right' :
33927                 case 'tall' :
33928                     
33929                     if(box.length){
33930                         boxes.push(box);
33931                         box = [];
33932                     }
33933                     
33934                     boxes.push([item]);
33935                     
33936                     break;
33937                     
33938                 case 'xs' :
33939                 case 'sm' :
33940                 case 'wide' :
33941                 case 'wide-thin' :
33942                     
33943                     box.push(item);
33944                     
33945                     break;
33946                 default :
33947                     break;
33948                     
33949             }
33950             
33951         }, this);
33952         
33953         if(box.length){
33954             boxes.push(box);
33955             box = [];
33956         }
33957         
33958         var filterPattern = function(box, length)
33959         {
33960             if(!box.length){
33961                 return;
33962             }
33963             
33964             var match = false;
33965             
33966             var pattern = box.slice(0, length);
33967             
33968             var format = [];
33969             
33970             Roo.each(pattern, function(i){
33971                 format.push(i.size);
33972             }, this);
33973             
33974             Roo.each(standard, function(s){
33975                 
33976                 if(String(s) != String(format)){
33977                     return;
33978                 }
33979                 
33980                 match = true;
33981                 return false;
33982                 
33983             }, this);
33984             
33985             if(!match && length == 1){
33986                 return;
33987             }
33988             
33989             if(!match){
33990                 filterPattern(box, length - 1);
33991                 return;
33992             }
33993                 
33994             queue.push(pattern);
33995
33996             box = box.slice(length, box.length);
33997
33998             filterPattern(box, 4);
33999
34000             return;
34001             
34002         }
34003         
34004         Roo.each(boxes, function(box, k){
34005             
34006             if(!box.length){
34007                 return;
34008             }
34009             
34010             if(box.length == 1){
34011                 queue.push(box);
34012                 return;
34013             }
34014             
34015             filterPattern(box, 4);
34016             
34017         }, this);
34018         
34019         
34020         var prune = [];
34021         
34022         var pos = this.el.getBox(true);
34023         
34024         var minX = pos.x;
34025         
34026         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34027         
34028         var hit_end = false;
34029         
34030         Roo.each(queue, function(box){
34031             
34032             if(hit_end){
34033                 
34034                 Roo.each(box, function(b){
34035                 
34036                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34037                     b.el.hide();
34038
34039                 }, this);
34040
34041                 return;
34042             }
34043             
34044             var mx = 0;
34045             
34046             Roo.each(box, function(b){
34047                 
34048                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34049                 b.el.show();
34050
34051                 mx = Math.max(mx, b.x);
34052                 
34053             }, this);
34054             
34055             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34056             
34057             if(maxX < minX){
34058                 
34059                 Roo.each(box, function(b){
34060                 
34061                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34062                     b.el.hide();
34063                     
34064                 }, this);
34065                 
34066                 hit_end = true;
34067                 
34068                 return;
34069             }
34070             
34071             prune.push(box);
34072             
34073         }, this);
34074         
34075         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34076     },
34077     
34078     /** Sets position of item in DOM
34079     * @param {Element} item
34080     * @param {Number} x - horizontal position
34081     * @param {Number} y - vertical position
34082     * @param {Boolean} isInstant - disables transitions
34083     */
34084     _processVerticalLayoutQueue : function( queue, isInstant )
34085     {
34086         var pos = this.el.getBox(true);
34087         var x = pos.x;
34088         var y = pos.y;
34089         var maxY = [];
34090         
34091         for (var i = 0; i < this.cols; i++){
34092             maxY[i] = pos.y;
34093         }
34094         
34095         Roo.each(queue, function(box, k){
34096             
34097             var col = k % this.cols;
34098             
34099             Roo.each(box, function(b,kk){
34100                 
34101                 b.el.position('absolute');
34102                 
34103                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34104                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34105                 
34106                 if(b.size == 'md-left' || b.size == 'md-right'){
34107                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34108                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34109                 }
34110                 
34111                 b.el.setWidth(width);
34112                 b.el.setHeight(height);
34113                 // iframe?
34114                 b.el.select('iframe',true).setSize(width,height);
34115                 
34116             }, this);
34117             
34118             for (var i = 0; i < this.cols; i++){
34119                 
34120                 if(maxY[i] < maxY[col]){
34121                     col = i;
34122                     continue;
34123                 }
34124                 
34125                 col = Math.min(col, i);
34126                 
34127             }
34128             
34129             x = pos.x + col * (this.colWidth + this.padWidth);
34130             
34131             y = maxY[col];
34132             
34133             var positions = [];
34134             
34135             switch (box.length){
34136                 case 1 :
34137                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34138                     break;
34139                 case 2 :
34140                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34141                     break;
34142                 case 3 :
34143                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34144                     break;
34145                 case 4 :
34146                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34147                     break;
34148                 default :
34149                     break;
34150             }
34151             
34152             Roo.each(box, function(b,kk){
34153                 
34154                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34155                 
34156                 var sz = b.el.getSize();
34157                 
34158                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34159                 
34160             }, this);
34161             
34162         }, this);
34163         
34164         var mY = 0;
34165         
34166         for (var i = 0; i < this.cols; i++){
34167             mY = Math.max(mY, maxY[i]);
34168         }
34169         
34170         this.el.setHeight(mY - pos.y);
34171         
34172     },
34173     
34174 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34175 //    {
34176 //        var pos = this.el.getBox(true);
34177 //        var x = pos.x;
34178 //        var y = pos.y;
34179 //        var maxX = pos.right;
34180 //        
34181 //        var maxHeight = 0;
34182 //        
34183 //        Roo.each(items, function(item, k){
34184 //            
34185 //            var c = k % 2;
34186 //            
34187 //            item.el.position('absolute');
34188 //                
34189 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34190 //
34191 //            item.el.setWidth(width);
34192 //
34193 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34194 //
34195 //            item.el.setHeight(height);
34196 //            
34197 //            if(c == 0){
34198 //                item.el.setXY([x, y], isInstant ? false : true);
34199 //            } else {
34200 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34201 //            }
34202 //            
34203 //            y = y + height + this.alternativePadWidth;
34204 //            
34205 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34206 //            
34207 //        }, this);
34208 //        
34209 //        this.el.setHeight(maxHeight);
34210 //        
34211 //    },
34212     
34213     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34214     {
34215         var pos = this.el.getBox(true);
34216         
34217         var minX = pos.x;
34218         var minY = pos.y;
34219         
34220         var maxX = pos.right;
34221         
34222         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34223         
34224         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34225         
34226         Roo.each(queue, function(box, k){
34227             
34228             Roo.each(box, function(b, kk){
34229                 
34230                 b.el.position('absolute');
34231                 
34232                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34233                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34234                 
34235                 if(b.size == 'md-left' || b.size == 'md-right'){
34236                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34237                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34238                 }
34239                 
34240                 b.el.setWidth(width);
34241                 b.el.setHeight(height);
34242                 
34243             }, this);
34244             
34245             if(!box.length){
34246                 return;
34247             }
34248             
34249             var positions = [];
34250             
34251             switch (box.length){
34252                 case 1 :
34253                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34254                     break;
34255                 case 2 :
34256                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34257                     break;
34258                 case 3 :
34259                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34260                     break;
34261                 case 4 :
34262                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34263                     break;
34264                 default :
34265                     break;
34266             }
34267             
34268             Roo.each(box, function(b,kk){
34269                 
34270                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34271                 
34272                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34273                 
34274             }, this);
34275             
34276         }, this);
34277         
34278     },
34279     
34280     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34281     {
34282         Roo.each(eItems, function(b,k){
34283             
34284             b.size = (k == 0) ? 'sm' : 'xs';
34285             b.x = (k == 0) ? 2 : 1;
34286             b.y = (k == 0) ? 2 : 1;
34287             
34288             b.el.position('absolute');
34289             
34290             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34291                 
34292             b.el.setWidth(width);
34293             
34294             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34295             
34296             b.el.setHeight(height);
34297             
34298         }, this);
34299
34300         var positions = [];
34301         
34302         positions.push({
34303             x : maxX - this.unitWidth * 2 - this.gutter,
34304             y : minY
34305         });
34306         
34307         positions.push({
34308             x : maxX - this.unitWidth,
34309             y : minY + (this.unitWidth + this.gutter) * 2
34310         });
34311         
34312         positions.push({
34313             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34314             y : minY
34315         });
34316         
34317         Roo.each(eItems, function(b,k){
34318             
34319             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34320
34321         }, this);
34322         
34323     },
34324     
34325     getVerticalOneBoxColPositions : function(x, y, box)
34326     {
34327         var pos = [];
34328         
34329         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34330         
34331         if(box[0].size == 'md-left'){
34332             rand = 0;
34333         }
34334         
34335         if(box[0].size == 'md-right'){
34336             rand = 1;
34337         }
34338         
34339         pos.push({
34340             x : x + (this.unitWidth + this.gutter) * rand,
34341             y : y
34342         });
34343         
34344         return pos;
34345     },
34346     
34347     getVerticalTwoBoxColPositions : function(x, y, box)
34348     {
34349         var pos = [];
34350         
34351         if(box[0].size == 'xs'){
34352             
34353             pos.push({
34354                 x : x,
34355                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34356             });
34357
34358             pos.push({
34359                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34360                 y : y
34361             });
34362             
34363             return pos;
34364             
34365         }
34366         
34367         pos.push({
34368             x : x,
34369             y : y
34370         });
34371
34372         pos.push({
34373             x : x + (this.unitWidth + this.gutter) * 2,
34374             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34375         });
34376         
34377         return pos;
34378         
34379     },
34380     
34381     getVerticalThreeBoxColPositions : function(x, y, box)
34382     {
34383         var pos = [];
34384         
34385         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34386             
34387             pos.push({
34388                 x : x,
34389                 y : y
34390             });
34391
34392             pos.push({
34393                 x : x + (this.unitWidth + this.gutter) * 1,
34394                 y : y
34395             });
34396             
34397             pos.push({
34398                 x : x + (this.unitWidth + this.gutter) * 2,
34399                 y : y
34400             });
34401             
34402             return pos;
34403             
34404         }
34405         
34406         if(box[0].size == 'xs' && box[1].size == 'xs'){
34407             
34408             pos.push({
34409                 x : x,
34410                 y : y
34411             });
34412
34413             pos.push({
34414                 x : x,
34415                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34416             });
34417             
34418             pos.push({
34419                 x : x + (this.unitWidth + this.gutter) * 1,
34420                 y : y
34421             });
34422             
34423             return pos;
34424             
34425         }
34426         
34427         pos.push({
34428             x : x,
34429             y : y
34430         });
34431
34432         pos.push({
34433             x : x + (this.unitWidth + this.gutter) * 2,
34434             y : y
34435         });
34436
34437         pos.push({
34438             x : x + (this.unitWidth + this.gutter) * 2,
34439             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34440         });
34441             
34442         return pos;
34443         
34444     },
34445     
34446     getVerticalFourBoxColPositions : function(x, y, box)
34447     {
34448         var pos = [];
34449         
34450         if(box[0].size == 'xs'){
34451             
34452             pos.push({
34453                 x : x,
34454                 y : y
34455             });
34456
34457             pos.push({
34458                 x : x,
34459                 y : y + (this.unitHeight + this.gutter) * 1
34460             });
34461             
34462             pos.push({
34463                 x : x,
34464                 y : y + (this.unitHeight + this.gutter) * 2
34465             });
34466             
34467             pos.push({
34468                 x : x + (this.unitWidth + this.gutter) * 1,
34469                 y : y
34470             });
34471             
34472             return pos;
34473             
34474         }
34475         
34476         pos.push({
34477             x : x,
34478             y : y
34479         });
34480
34481         pos.push({
34482             x : x + (this.unitWidth + this.gutter) * 2,
34483             y : y
34484         });
34485
34486         pos.push({
34487             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34488             y : y + (this.unitHeight + this.gutter) * 1
34489         });
34490
34491         pos.push({
34492             x : x + (this.unitWidth + this.gutter) * 2,
34493             y : y + (this.unitWidth + this.gutter) * 2
34494         });
34495
34496         return pos;
34497         
34498     },
34499     
34500     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34501     {
34502         var pos = [];
34503         
34504         if(box[0].size == 'md-left'){
34505             pos.push({
34506                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34507                 y : minY
34508             });
34509             
34510             return pos;
34511         }
34512         
34513         if(box[0].size == 'md-right'){
34514             pos.push({
34515                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34516                 y : minY + (this.unitWidth + this.gutter) * 1
34517             });
34518             
34519             return pos;
34520         }
34521         
34522         var rand = Math.floor(Math.random() * (4 - box[0].y));
34523         
34524         pos.push({
34525             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34526             y : minY + (this.unitWidth + this.gutter) * rand
34527         });
34528         
34529         return pos;
34530         
34531     },
34532     
34533     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34534     {
34535         var pos = [];
34536         
34537         if(box[0].size == 'xs'){
34538             
34539             pos.push({
34540                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34541                 y : minY
34542             });
34543
34544             pos.push({
34545                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34546                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34547             });
34548             
34549             return pos;
34550             
34551         }
34552         
34553         pos.push({
34554             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34555             y : minY
34556         });
34557
34558         pos.push({
34559             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34560             y : minY + (this.unitWidth + this.gutter) * 2
34561         });
34562         
34563         return pos;
34564         
34565     },
34566     
34567     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34568     {
34569         var pos = [];
34570         
34571         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34572             
34573             pos.push({
34574                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34575                 y : minY
34576             });
34577
34578             pos.push({
34579                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34580                 y : minY + (this.unitWidth + this.gutter) * 1
34581             });
34582             
34583             pos.push({
34584                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34585                 y : minY + (this.unitWidth + this.gutter) * 2
34586             });
34587             
34588             return pos;
34589             
34590         }
34591         
34592         if(box[0].size == 'xs' && box[1].size == 'xs'){
34593             
34594             pos.push({
34595                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34596                 y : minY
34597             });
34598
34599             pos.push({
34600                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34601                 y : minY
34602             });
34603             
34604             pos.push({
34605                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34606                 y : minY + (this.unitWidth + this.gutter) * 1
34607             });
34608             
34609             return pos;
34610             
34611         }
34612         
34613         pos.push({
34614             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34615             y : minY
34616         });
34617
34618         pos.push({
34619             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34620             y : minY + (this.unitWidth + this.gutter) * 2
34621         });
34622
34623         pos.push({
34624             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34625             y : minY + (this.unitWidth + this.gutter) * 2
34626         });
34627             
34628         return pos;
34629         
34630     },
34631     
34632     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34633     {
34634         var pos = [];
34635         
34636         if(box[0].size == 'xs'){
34637             
34638             pos.push({
34639                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34640                 y : minY
34641             });
34642
34643             pos.push({
34644                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34645                 y : minY
34646             });
34647             
34648             pos.push({
34649                 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),
34650                 y : minY
34651             });
34652             
34653             pos.push({
34654                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34655                 y : minY + (this.unitWidth + this.gutter) * 1
34656             });
34657             
34658             return pos;
34659             
34660         }
34661         
34662         pos.push({
34663             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34664             y : minY
34665         });
34666         
34667         pos.push({
34668             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34669             y : minY + (this.unitWidth + this.gutter) * 2
34670         });
34671         
34672         pos.push({
34673             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34674             y : minY + (this.unitWidth + this.gutter) * 2
34675         });
34676         
34677         pos.push({
34678             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),
34679             y : minY + (this.unitWidth + this.gutter) * 2
34680         });
34681
34682         return pos;
34683         
34684     },
34685     
34686     /**
34687     * remove a Masonry Brick
34688     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34689     */
34690     removeBrick : function(brick_id)
34691     {
34692         if (!brick_id) {
34693             return;
34694         }
34695         
34696         for (var i = 0; i<this.bricks.length; i++) {
34697             if (this.bricks[i].id == brick_id) {
34698                 this.bricks.splice(i,1);
34699                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34700                 this.initial();
34701             }
34702         }
34703     },
34704     
34705     /**
34706     * adds a Masonry Brick
34707     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34708     */
34709     addBrick : function(cfg)
34710     {
34711         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34712         //this.register(cn);
34713         cn.parentId = this.id;
34714         cn.render(this.el);
34715         return cn;
34716     },
34717     
34718     /**
34719     * register a Masonry Brick
34720     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34721     */
34722     
34723     register : function(brick)
34724     {
34725         this.bricks.push(brick);
34726         brick.masonryId = this.id;
34727     },
34728     
34729     /**
34730     * clear all the Masonry Brick
34731     */
34732     clearAll : function()
34733     {
34734         this.bricks = [];
34735         //this.getChildContainer().dom.innerHTML = "";
34736         this.el.dom.innerHTML = '';
34737     },
34738     
34739     getSelected : function()
34740     {
34741         if (!this.selectedBrick) {
34742             return false;
34743         }
34744         
34745         return this.selectedBrick;
34746     }
34747 });
34748
34749 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34750     
34751     groups: {},
34752      /**
34753     * register a Masonry Layout
34754     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34755     */
34756     
34757     register : function(layout)
34758     {
34759         this.groups[layout.id] = layout;
34760     },
34761     /**
34762     * fetch a  Masonry Layout based on the masonry layout ID
34763     * @param {string} the masonry layout to add
34764     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34765     */
34766     
34767     get: function(layout_id) {
34768         if (typeof(this.groups[layout_id]) == 'undefined') {
34769             return false;
34770         }
34771         return this.groups[layout_id] ;
34772     }
34773     
34774     
34775     
34776 });
34777
34778  
34779
34780  /**
34781  *
34782  * This is based on 
34783  * http://masonry.desandro.com
34784  *
34785  * The idea is to render all the bricks based on vertical width...
34786  *
34787  * The original code extends 'outlayer' - we might need to use that....
34788  * 
34789  */
34790
34791
34792 /**
34793  * @class Roo.bootstrap.LayoutMasonryAuto
34794  * @extends Roo.bootstrap.Component
34795  * Bootstrap Layout Masonry class
34796  * 
34797  * @constructor
34798  * Create a new Element
34799  * @param {Object} config The config object
34800  */
34801
34802 Roo.bootstrap.LayoutMasonryAuto = function(config){
34803     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34804 };
34805
34806 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34807     
34808       /**
34809      * @cfg {Boolean} isFitWidth  - resize the width..
34810      */   
34811     isFitWidth : false,  // options..
34812     /**
34813      * @cfg {Boolean} isOriginLeft = left align?
34814      */   
34815     isOriginLeft : true,
34816     /**
34817      * @cfg {Boolean} isOriginTop = top align?
34818      */   
34819     isOriginTop : false,
34820     /**
34821      * @cfg {Boolean} isLayoutInstant = no animation?
34822      */   
34823     isLayoutInstant : false, // needed?
34824     /**
34825      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34826      */   
34827     isResizingContainer : true,
34828     /**
34829      * @cfg {Number} columnWidth  width of the columns 
34830      */   
34831     
34832     columnWidth : 0,
34833     
34834     /**
34835      * @cfg {Number} maxCols maximum number of columns
34836      */   
34837     
34838     maxCols: 0,
34839     /**
34840      * @cfg {Number} padHeight padding below box..
34841      */   
34842     
34843     padHeight : 10, 
34844     
34845     /**
34846      * @cfg {Boolean} isAutoInitial defalut true
34847      */   
34848     
34849     isAutoInitial : true, 
34850     
34851     // private?
34852     gutter : 0,
34853     
34854     containerWidth: 0,
34855     initialColumnWidth : 0,
34856     currentSize : null,
34857     
34858     colYs : null, // array.
34859     maxY : 0,
34860     padWidth: 10,
34861     
34862     
34863     tag: 'div',
34864     cls: '',
34865     bricks: null, //CompositeElement
34866     cols : 0, // array?
34867     // element : null, // wrapped now this.el
34868     _isLayoutInited : null, 
34869     
34870     
34871     getAutoCreate : function(){
34872         
34873         var cfg = {
34874             tag: this.tag,
34875             cls: 'blog-masonary-wrapper ' + this.cls,
34876             cn : {
34877                 cls : 'mas-boxes masonary'
34878             }
34879         };
34880         
34881         return cfg;
34882     },
34883     
34884     getChildContainer: function( )
34885     {
34886         if (this.boxesEl) {
34887             return this.boxesEl;
34888         }
34889         
34890         this.boxesEl = this.el.select('.mas-boxes').first();
34891         
34892         return this.boxesEl;
34893     },
34894     
34895     
34896     initEvents : function()
34897     {
34898         var _this = this;
34899         
34900         if(this.isAutoInitial){
34901             Roo.log('hook children rendered');
34902             this.on('childrenrendered', function() {
34903                 Roo.log('children rendered');
34904                 _this.initial();
34905             } ,this);
34906         }
34907         
34908     },
34909     
34910     initial : function()
34911     {
34912         this.reloadItems();
34913
34914         this.currentSize = this.el.getBox(true);
34915
34916         /// was window resize... - let's see if this works..
34917         Roo.EventManager.onWindowResize(this.resize, this); 
34918
34919         if(!this.isAutoInitial){
34920             this.layout();
34921             return;
34922         }
34923         
34924         this.layout.defer(500,this);
34925     },
34926     
34927     reloadItems: function()
34928     {
34929         this.bricks = this.el.select('.masonry-brick', true);
34930         
34931         this.bricks.each(function(b) {
34932             //Roo.log(b.getSize());
34933             if (!b.attr('originalwidth')) {
34934                 b.attr('originalwidth',  b.getSize().width);
34935             }
34936             
34937         });
34938         
34939         Roo.log(this.bricks.elements.length);
34940     },
34941     
34942     resize : function()
34943     {
34944         Roo.log('resize');
34945         var cs = this.el.getBox(true);
34946         
34947         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34948             Roo.log("no change in with or X");
34949             return;
34950         }
34951         this.currentSize = cs;
34952         this.layout();
34953     },
34954     
34955     layout : function()
34956     {
34957          Roo.log('layout');
34958         this._resetLayout();
34959         //this._manageStamps();
34960       
34961         // don't animate first layout
34962         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34963         this.layoutItems( isInstant );
34964       
34965         // flag for initalized
34966         this._isLayoutInited = true;
34967     },
34968     
34969     layoutItems : function( isInstant )
34970     {
34971         //var items = this._getItemsForLayout( this.items );
34972         // original code supports filtering layout items.. we just ignore it..
34973         
34974         this._layoutItems( this.bricks , isInstant );
34975       
34976         this._postLayout();
34977     },
34978     _layoutItems : function ( items , isInstant)
34979     {
34980        //this.fireEvent( 'layout', this, items );
34981     
34982
34983         if ( !items || !items.elements.length ) {
34984           // no items, emit event with empty array
34985             return;
34986         }
34987
34988         var queue = [];
34989         items.each(function(item) {
34990             Roo.log("layout item");
34991             Roo.log(item);
34992             // get x/y object from method
34993             var position = this._getItemLayoutPosition( item );
34994             // enqueue
34995             position.item = item;
34996             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34997             queue.push( position );
34998         }, this);
34999       
35000         this._processLayoutQueue( queue );
35001     },
35002     /** Sets position of item in DOM
35003     * @param {Element} item
35004     * @param {Number} x - horizontal position
35005     * @param {Number} y - vertical position
35006     * @param {Boolean} isInstant - disables transitions
35007     */
35008     _processLayoutQueue : function( queue )
35009     {
35010         for ( var i=0, len = queue.length; i < len; i++ ) {
35011             var obj = queue[i];
35012             obj.item.position('absolute');
35013             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35014         }
35015     },
35016       
35017     
35018     /**
35019     * Any logic you want to do after each layout,
35020     * i.e. size the container
35021     */
35022     _postLayout : function()
35023     {
35024         this.resizeContainer();
35025     },
35026     
35027     resizeContainer : function()
35028     {
35029         if ( !this.isResizingContainer ) {
35030             return;
35031         }
35032         var size = this._getContainerSize();
35033         if ( size ) {
35034             this.el.setSize(size.width,size.height);
35035             this.boxesEl.setSize(size.width,size.height);
35036         }
35037     },
35038     
35039     
35040     
35041     _resetLayout : function()
35042     {
35043         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35044         this.colWidth = this.el.getWidth();
35045         //this.gutter = this.el.getWidth(); 
35046         
35047         this.measureColumns();
35048
35049         // reset column Y
35050         var i = this.cols;
35051         this.colYs = [];
35052         while (i--) {
35053             this.colYs.push( 0 );
35054         }
35055     
35056         this.maxY = 0;
35057     },
35058
35059     measureColumns : function()
35060     {
35061         this.getContainerWidth();
35062       // if columnWidth is 0, default to outerWidth of first item
35063         if ( !this.columnWidth ) {
35064             var firstItem = this.bricks.first();
35065             Roo.log(firstItem);
35066             this.columnWidth  = this.containerWidth;
35067             if (firstItem && firstItem.attr('originalwidth') ) {
35068                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35069             }
35070             // columnWidth fall back to item of first element
35071             Roo.log("set column width?");
35072                         this.initialColumnWidth = this.columnWidth  ;
35073
35074             // if first elem has no width, default to size of container
35075             
35076         }
35077         
35078         
35079         if (this.initialColumnWidth) {
35080             this.columnWidth = this.initialColumnWidth;
35081         }
35082         
35083         
35084             
35085         // column width is fixed at the top - however if container width get's smaller we should
35086         // reduce it...
35087         
35088         // this bit calcs how man columns..
35089             
35090         var columnWidth = this.columnWidth += this.gutter;
35091       
35092         // calculate columns
35093         var containerWidth = this.containerWidth + this.gutter;
35094         
35095         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35096         // fix rounding errors, typically with gutters
35097         var excess = columnWidth - containerWidth % columnWidth;
35098         
35099         
35100         // if overshoot is less than a pixel, round up, otherwise floor it
35101         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35102         cols = Math[ mathMethod ]( cols );
35103         this.cols = Math.max( cols, 1 );
35104         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35105         
35106          // padding positioning..
35107         var totalColWidth = this.cols * this.columnWidth;
35108         var padavail = this.containerWidth - totalColWidth;
35109         // so for 2 columns - we need 3 'pads'
35110         
35111         var padNeeded = (1+this.cols) * this.padWidth;
35112         
35113         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35114         
35115         this.columnWidth += padExtra
35116         //this.padWidth = Math.floor(padavail /  ( this.cols));
35117         
35118         // adjust colum width so that padding is fixed??
35119         
35120         // we have 3 columns ... total = width * 3
35121         // we have X left over... that should be used by 
35122         
35123         //if (this.expandC) {
35124             
35125         //}
35126         
35127         
35128         
35129     },
35130     
35131     getContainerWidth : function()
35132     {
35133        /* // container is parent if fit width
35134         var container = this.isFitWidth ? this.element.parentNode : this.element;
35135         // check that this.size and size are there
35136         // IE8 triggers resize on body size change, so they might not be
35137         
35138         var size = getSize( container );  //FIXME
35139         this.containerWidth = size && size.innerWidth; //FIXME
35140         */
35141          
35142         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35143         
35144     },
35145     
35146     _getItemLayoutPosition : function( item )  // what is item?
35147     {
35148         // we resize the item to our columnWidth..
35149       
35150         item.setWidth(this.columnWidth);
35151         item.autoBoxAdjust  = false;
35152         
35153         var sz = item.getSize();
35154  
35155         // how many columns does this brick span
35156         var remainder = this.containerWidth % this.columnWidth;
35157         
35158         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35159         // round if off by 1 pixel, otherwise use ceil
35160         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35161         colSpan = Math.min( colSpan, this.cols );
35162         
35163         // normally this should be '1' as we dont' currently allow multi width columns..
35164         
35165         var colGroup = this._getColGroup( colSpan );
35166         // get the minimum Y value from the columns
35167         var minimumY = Math.min.apply( Math, colGroup );
35168         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35169         
35170         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35171          
35172         // position the brick
35173         var position = {
35174             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35175             y: this.currentSize.y + minimumY + this.padHeight
35176         };
35177         
35178         Roo.log(position);
35179         // apply setHeight to necessary columns
35180         var setHeight = minimumY + sz.height + this.padHeight;
35181         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35182         
35183         var setSpan = this.cols + 1 - colGroup.length;
35184         for ( var i = 0; i < setSpan; i++ ) {
35185           this.colYs[ shortColIndex + i ] = setHeight ;
35186         }
35187       
35188         return position;
35189     },
35190     
35191     /**
35192      * @param {Number} colSpan - number of columns the element spans
35193      * @returns {Array} colGroup
35194      */
35195     _getColGroup : function( colSpan )
35196     {
35197         if ( colSpan < 2 ) {
35198           // if brick spans only one column, use all the column Ys
35199           return this.colYs;
35200         }
35201       
35202         var colGroup = [];
35203         // how many different places could this brick fit horizontally
35204         var groupCount = this.cols + 1 - colSpan;
35205         // for each group potential horizontal position
35206         for ( var i = 0; i < groupCount; i++ ) {
35207           // make an array of colY values for that one group
35208           var groupColYs = this.colYs.slice( i, i + colSpan );
35209           // and get the max value of the array
35210           colGroup[i] = Math.max.apply( Math, groupColYs );
35211         }
35212         return colGroup;
35213     },
35214     /*
35215     _manageStamp : function( stamp )
35216     {
35217         var stampSize =  stamp.getSize();
35218         var offset = stamp.getBox();
35219         // get the columns that this stamp affects
35220         var firstX = this.isOriginLeft ? offset.x : offset.right;
35221         var lastX = firstX + stampSize.width;
35222         var firstCol = Math.floor( firstX / this.columnWidth );
35223         firstCol = Math.max( 0, firstCol );
35224         
35225         var lastCol = Math.floor( lastX / this.columnWidth );
35226         // lastCol should not go over if multiple of columnWidth #425
35227         lastCol -= lastX % this.columnWidth ? 0 : 1;
35228         lastCol = Math.min( this.cols - 1, lastCol );
35229         
35230         // set colYs to bottom of the stamp
35231         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35232             stampSize.height;
35233             
35234         for ( var i = firstCol; i <= lastCol; i++ ) {
35235           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35236         }
35237     },
35238     */
35239     
35240     _getContainerSize : function()
35241     {
35242         this.maxY = Math.max.apply( Math, this.colYs );
35243         var size = {
35244             height: this.maxY
35245         };
35246       
35247         if ( this.isFitWidth ) {
35248             size.width = this._getContainerFitWidth();
35249         }
35250       
35251         return size;
35252     },
35253     
35254     _getContainerFitWidth : function()
35255     {
35256         var unusedCols = 0;
35257         // count unused columns
35258         var i = this.cols;
35259         while ( --i ) {
35260           if ( this.colYs[i] !== 0 ) {
35261             break;
35262           }
35263           unusedCols++;
35264         }
35265         // fit container to columns that have been used
35266         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35267     },
35268     
35269     needsResizeLayout : function()
35270     {
35271         var previousWidth = this.containerWidth;
35272         this.getContainerWidth();
35273         return previousWidth !== this.containerWidth;
35274     }
35275  
35276 });
35277
35278  
35279
35280  /*
35281  * - LGPL
35282  *
35283  * element
35284  * 
35285  */
35286
35287 /**
35288  * @class Roo.bootstrap.MasonryBrick
35289  * @extends Roo.bootstrap.Component
35290  * Bootstrap MasonryBrick class
35291  * 
35292  * @constructor
35293  * Create a new MasonryBrick
35294  * @param {Object} config The config object
35295  */
35296
35297 Roo.bootstrap.MasonryBrick = function(config){
35298     
35299     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35300     
35301     Roo.bootstrap.MasonryBrick.register(this);
35302     
35303     this.addEvents({
35304         // raw events
35305         /**
35306          * @event click
35307          * When a MasonryBrick is clcik
35308          * @param {Roo.bootstrap.MasonryBrick} this
35309          * @param {Roo.EventObject} e
35310          */
35311         "click" : true
35312     });
35313 };
35314
35315 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35316     
35317     /**
35318      * @cfg {String} title
35319      */   
35320     title : '',
35321     /**
35322      * @cfg {String} html
35323      */   
35324     html : '',
35325     /**
35326      * @cfg {String} bgimage
35327      */   
35328     bgimage : '',
35329     /**
35330      * @cfg {String} videourl
35331      */   
35332     videourl : '',
35333     /**
35334      * @cfg {String} cls
35335      */   
35336     cls : '',
35337     /**
35338      * @cfg {String} href
35339      */   
35340     href : '',
35341     /**
35342      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35343      */   
35344     size : 'xs',
35345     
35346     /**
35347      * @cfg {String} placetitle (center|bottom)
35348      */   
35349     placetitle : '',
35350     
35351     /**
35352      * @cfg {Boolean} isFitContainer defalut true
35353      */   
35354     isFitContainer : true, 
35355     
35356     /**
35357      * @cfg {Boolean} preventDefault defalut false
35358      */   
35359     preventDefault : false, 
35360     
35361     /**
35362      * @cfg {Boolean} inverse defalut false
35363      */   
35364     maskInverse : false, 
35365     
35366     getAutoCreate : function()
35367     {
35368         if(!this.isFitContainer){
35369             return this.getSplitAutoCreate();
35370         }
35371         
35372         var cls = 'masonry-brick masonry-brick-full';
35373         
35374         if(this.href.length){
35375             cls += ' masonry-brick-link';
35376         }
35377         
35378         if(this.bgimage.length){
35379             cls += ' masonry-brick-image';
35380         }
35381         
35382         if(this.maskInverse){
35383             cls += ' mask-inverse';
35384         }
35385         
35386         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35387             cls += ' enable-mask';
35388         }
35389         
35390         if(this.size){
35391             cls += ' masonry-' + this.size + '-brick';
35392         }
35393         
35394         if(this.placetitle.length){
35395             
35396             switch (this.placetitle) {
35397                 case 'center' :
35398                     cls += ' masonry-center-title';
35399                     break;
35400                 case 'bottom' :
35401                     cls += ' masonry-bottom-title';
35402                     break;
35403                 default:
35404                     break;
35405             }
35406             
35407         } else {
35408             if(!this.html.length && !this.bgimage.length){
35409                 cls += ' masonry-center-title';
35410             }
35411
35412             if(!this.html.length && this.bgimage.length){
35413                 cls += ' masonry-bottom-title';
35414             }
35415         }
35416         
35417         if(this.cls){
35418             cls += ' ' + this.cls;
35419         }
35420         
35421         var cfg = {
35422             tag: (this.href.length) ? 'a' : 'div',
35423             cls: cls,
35424             cn: [
35425                 {
35426                     tag: 'div',
35427                     cls: 'masonry-brick-mask'
35428                 },
35429                 {
35430                     tag: 'div',
35431                     cls: 'masonry-brick-paragraph',
35432                     cn: []
35433                 }
35434             ]
35435         };
35436         
35437         if(this.href.length){
35438             cfg.href = this.href;
35439         }
35440         
35441         var cn = cfg.cn[1].cn;
35442         
35443         if(this.title.length){
35444             cn.push({
35445                 tag: 'h4',
35446                 cls: 'masonry-brick-title',
35447                 html: this.title
35448             });
35449         }
35450         
35451         if(this.html.length){
35452             cn.push({
35453                 tag: 'p',
35454                 cls: 'masonry-brick-text',
35455                 html: this.html
35456             });
35457         }
35458         
35459         if (!this.title.length && !this.html.length) {
35460             cfg.cn[1].cls += ' hide';
35461         }
35462         
35463         if(this.bgimage.length){
35464             cfg.cn.push({
35465                 tag: 'img',
35466                 cls: 'masonry-brick-image-view',
35467                 src: this.bgimage
35468             });
35469         }
35470         
35471         if(this.videourl.length){
35472             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35473             // youtube support only?
35474             cfg.cn.push({
35475                 tag: 'iframe',
35476                 cls: 'masonry-brick-image-view',
35477                 src: vurl,
35478                 frameborder : 0,
35479                 allowfullscreen : true
35480             });
35481         }
35482         
35483         return cfg;
35484         
35485     },
35486     
35487     getSplitAutoCreate : function()
35488     {
35489         var cls = 'masonry-brick masonry-brick-split';
35490         
35491         if(this.href.length){
35492             cls += ' masonry-brick-link';
35493         }
35494         
35495         if(this.bgimage.length){
35496             cls += ' masonry-brick-image';
35497         }
35498         
35499         if(this.size){
35500             cls += ' masonry-' + this.size + '-brick';
35501         }
35502         
35503         switch (this.placetitle) {
35504             case 'center' :
35505                 cls += ' masonry-center-title';
35506                 break;
35507             case 'bottom' :
35508                 cls += ' masonry-bottom-title';
35509                 break;
35510             default:
35511                 if(!this.bgimage.length){
35512                     cls += ' masonry-center-title';
35513                 }
35514
35515                 if(this.bgimage.length){
35516                     cls += ' masonry-bottom-title';
35517                 }
35518                 break;
35519         }
35520         
35521         if(this.cls){
35522             cls += ' ' + this.cls;
35523         }
35524         
35525         var cfg = {
35526             tag: (this.href.length) ? 'a' : 'div',
35527             cls: cls,
35528             cn: [
35529                 {
35530                     tag: 'div',
35531                     cls: 'masonry-brick-split-head',
35532                     cn: [
35533                         {
35534                             tag: 'div',
35535                             cls: 'masonry-brick-paragraph',
35536                             cn: []
35537                         }
35538                     ]
35539                 },
35540                 {
35541                     tag: 'div',
35542                     cls: 'masonry-brick-split-body',
35543                     cn: []
35544                 }
35545             ]
35546         };
35547         
35548         if(this.href.length){
35549             cfg.href = this.href;
35550         }
35551         
35552         if(this.title.length){
35553             cfg.cn[0].cn[0].cn.push({
35554                 tag: 'h4',
35555                 cls: 'masonry-brick-title',
35556                 html: this.title
35557             });
35558         }
35559         
35560         if(this.html.length){
35561             cfg.cn[1].cn.push({
35562                 tag: 'p',
35563                 cls: 'masonry-brick-text',
35564                 html: this.html
35565             });
35566         }
35567
35568         if(this.bgimage.length){
35569             cfg.cn[0].cn.push({
35570                 tag: 'img',
35571                 cls: 'masonry-brick-image-view',
35572                 src: this.bgimage
35573             });
35574         }
35575         
35576         if(this.videourl.length){
35577             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35578             // youtube support only?
35579             cfg.cn[0].cn.cn.push({
35580                 tag: 'iframe',
35581                 cls: 'masonry-brick-image-view',
35582                 src: vurl,
35583                 frameborder : 0,
35584                 allowfullscreen : true
35585             });
35586         }
35587         
35588         return cfg;
35589     },
35590     
35591     initEvents: function() 
35592     {
35593         switch (this.size) {
35594             case 'xs' :
35595                 this.x = 1;
35596                 this.y = 1;
35597                 break;
35598             case 'sm' :
35599                 this.x = 2;
35600                 this.y = 2;
35601                 break;
35602             case 'md' :
35603             case 'md-left' :
35604             case 'md-right' :
35605                 this.x = 3;
35606                 this.y = 3;
35607                 break;
35608             case 'tall' :
35609                 this.x = 2;
35610                 this.y = 3;
35611                 break;
35612             case 'wide' :
35613                 this.x = 3;
35614                 this.y = 2;
35615                 break;
35616             case 'wide-thin' :
35617                 this.x = 3;
35618                 this.y = 1;
35619                 break;
35620                         
35621             default :
35622                 break;
35623         }
35624         
35625         if(Roo.isTouch){
35626             this.el.on('touchstart', this.onTouchStart, this);
35627             this.el.on('touchmove', this.onTouchMove, this);
35628             this.el.on('touchend', this.onTouchEnd, this);
35629             this.el.on('contextmenu', this.onContextMenu, this);
35630         } else {
35631             this.el.on('mouseenter'  ,this.enter, this);
35632             this.el.on('mouseleave', this.leave, this);
35633             this.el.on('click', this.onClick, this);
35634         }
35635         
35636         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35637             this.parent().bricks.push(this);   
35638         }
35639         
35640     },
35641     
35642     onClick: function(e, el)
35643     {
35644         var time = this.endTimer - this.startTimer;
35645         // Roo.log(e.preventDefault());
35646         if(Roo.isTouch){
35647             if(time > 1000){
35648                 e.preventDefault();
35649                 return;
35650             }
35651         }
35652         
35653         if(!this.preventDefault){
35654             return;
35655         }
35656         
35657         e.preventDefault();
35658         
35659         if (this.activeClass != '') {
35660             this.selectBrick();
35661         }
35662         
35663         this.fireEvent('click', this, e);
35664     },
35665     
35666     enter: function(e, el)
35667     {
35668         e.preventDefault();
35669         
35670         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35671             return;
35672         }
35673         
35674         if(this.bgimage.length && this.html.length){
35675             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35676         }
35677     },
35678     
35679     leave: function(e, el)
35680     {
35681         e.preventDefault();
35682         
35683         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35684             return;
35685         }
35686         
35687         if(this.bgimage.length && this.html.length){
35688             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35689         }
35690     },
35691     
35692     onTouchStart: function(e, el)
35693     {
35694 //        e.preventDefault();
35695         
35696         this.touchmoved = false;
35697         
35698         if(!this.isFitContainer){
35699             return;
35700         }
35701         
35702         if(!this.bgimage.length || !this.html.length){
35703             return;
35704         }
35705         
35706         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35707         
35708         this.timer = new Date().getTime();
35709         
35710     },
35711     
35712     onTouchMove: function(e, el)
35713     {
35714         this.touchmoved = true;
35715     },
35716     
35717     onContextMenu : function(e,el)
35718     {
35719         e.preventDefault();
35720         e.stopPropagation();
35721         return false;
35722     },
35723     
35724     onTouchEnd: function(e, el)
35725     {
35726 //        e.preventDefault();
35727         
35728         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35729         
35730             this.leave(e,el);
35731             
35732             return;
35733         }
35734         
35735         if(!this.bgimage.length || !this.html.length){
35736             
35737             if(this.href.length){
35738                 window.location.href = this.href;
35739             }
35740             
35741             return;
35742         }
35743         
35744         if(!this.isFitContainer){
35745             return;
35746         }
35747         
35748         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35749         
35750         window.location.href = this.href;
35751     },
35752     
35753     //selection on single brick only
35754     selectBrick : function() {
35755         
35756         if (!this.parentId) {
35757             return;
35758         }
35759         
35760         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35761         var index = m.selectedBrick.indexOf(this.id);
35762         
35763         if ( index > -1) {
35764             m.selectedBrick.splice(index,1);
35765             this.el.removeClass(this.activeClass);
35766             return;
35767         }
35768         
35769         for(var i = 0; i < m.selectedBrick.length; i++) {
35770             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35771             b.el.removeClass(b.activeClass);
35772         }
35773         
35774         m.selectedBrick = [];
35775         
35776         m.selectedBrick.push(this.id);
35777         this.el.addClass(this.activeClass);
35778         return;
35779     },
35780     
35781     isSelected : function(){
35782         return this.el.hasClass(this.activeClass);
35783         
35784     }
35785 });
35786
35787 Roo.apply(Roo.bootstrap.MasonryBrick, {
35788     
35789     //groups: {},
35790     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35791      /**
35792     * register a Masonry Brick
35793     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35794     */
35795     
35796     register : function(brick)
35797     {
35798         //this.groups[brick.id] = brick;
35799         this.groups.add(brick.id, brick);
35800     },
35801     /**
35802     * fetch a  masonry brick based on the masonry brick ID
35803     * @param {string} the masonry brick to add
35804     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35805     */
35806     
35807     get: function(brick_id) 
35808     {
35809         // if (typeof(this.groups[brick_id]) == 'undefined') {
35810         //     return false;
35811         // }
35812         // return this.groups[brick_id] ;
35813         
35814         if(this.groups.key(brick_id)) {
35815             return this.groups.key(brick_id);
35816         }
35817         
35818         return false;
35819     }
35820     
35821     
35822     
35823 });
35824
35825  /*
35826  * - LGPL
35827  *
35828  * element
35829  * 
35830  */
35831
35832 /**
35833  * @class Roo.bootstrap.Brick
35834  * @extends Roo.bootstrap.Component
35835  * Bootstrap Brick class
35836  * 
35837  * @constructor
35838  * Create a new Brick
35839  * @param {Object} config The config object
35840  */
35841
35842 Roo.bootstrap.Brick = function(config){
35843     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35844     
35845     this.addEvents({
35846         // raw events
35847         /**
35848          * @event click
35849          * When a Brick is click
35850          * @param {Roo.bootstrap.Brick} this
35851          * @param {Roo.EventObject} e
35852          */
35853         "click" : true
35854     });
35855 };
35856
35857 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35858     
35859     /**
35860      * @cfg {String} title
35861      */   
35862     title : '',
35863     /**
35864      * @cfg {String} html
35865      */   
35866     html : '',
35867     /**
35868      * @cfg {String} bgimage
35869      */   
35870     bgimage : '',
35871     /**
35872      * @cfg {String} cls
35873      */   
35874     cls : '',
35875     /**
35876      * @cfg {String} href
35877      */   
35878     href : '',
35879     /**
35880      * @cfg {String} video
35881      */   
35882     video : '',
35883     /**
35884      * @cfg {Boolean} square
35885      */   
35886     square : true,
35887     
35888     getAutoCreate : function()
35889     {
35890         var cls = 'roo-brick';
35891         
35892         if(this.href.length){
35893             cls += ' roo-brick-link';
35894         }
35895         
35896         if(this.bgimage.length){
35897             cls += ' roo-brick-image';
35898         }
35899         
35900         if(!this.html.length && !this.bgimage.length){
35901             cls += ' roo-brick-center-title';
35902         }
35903         
35904         if(!this.html.length && this.bgimage.length){
35905             cls += ' roo-brick-bottom-title';
35906         }
35907         
35908         if(this.cls){
35909             cls += ' ' + this.cls;
35910         }
35911         
35912         var cfg = {
35913             tag: (this.href.length) ? 'a' : 'div',
35914             cls: cls,
35915             cn: [
35916                 {
35917                     tag: 'div',
35918                     cls: 'roo-brick-paragraph',
35919                     cn: []
35920                 }
35921             ]
35922         };
35923         
35924         if(this.href.length){
35925             cfg.href = this.href;
35926         }
35927         
35928         var cn = cfg.cn[0].cn;
35929         
35930         if(this.title.length){
35931             cn.push({
35932                 tag: 'h4',
35933                 cls: 'roo-brick-title',
35934                 html: this.title
35935             });
35936         }
35937         
35938         if(this.html.length){
35939             cn.push({
35940                 tag: 'p',
35941                 cls: 'roo-brick-text',
35942                 html: this.html
35943             });
35944         } else {
35945             cn.cls += ' hide';
35946         }
35947         
35948         if(this.bgimage.length){
35949             cfg.cn.push({
35950                 tag: 'img',
35951                 cls: 'roo-brick-image-view',
35952                 src: this.bgimage
35953             });
35954         }
35955         
35956         return cfg;
35957     },
35958     
35959     initEvents: function() 
35960     {
35961         if(this.title.length || this.html.length){
35962             this.el.on('mouseenter'  ,this.enter, this);
35963             this.el.on('mouseleave', this.leave, this);
35964         }
35965         
35966         Roo.EventManager.onWindowResize(this.resize, this); 
35967         
35968         if(this.bgimage.length){
35969             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35970             this.imageEl.on('load', this.onImageLoad, this);
35971             return;
35972         }
35973         
35974         this.resize();
35975     },
35976     
35977     onImageLoad : function()
35978     {
35979         this.resize();
35980     },
35981     
35982     resize : function()
35983     {
35984         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35985         
35986         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35987         
35988         if(this.bgimage.length){
35989             var image = this.el.select('.roo-brick-image-view', true).first();
35990             
35991             image.setWidth(paragraph.getWidth());
35992             
35993             if(this.square){
35994                 image.setHeight(paragraph.getWidth());
35995             }
35996             
35997             this.el.setHeight(image.getHeight());
35998             paragraph.setHeight(image.getHeight());
35999             
36000         }
36001         
36002     },
36003     
36004     enter: function(e, el)
36005     {
36006         e.preventDefault();
36007         
36008         if(this.bgimage.length){
36009             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36010             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36011         }
36012     },
36013     
36014     leave: function(e, el)
36015     {
36016         e.preventDefault();
36017         
36018         if(this.bgimage.length){
36019             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36020             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36021         }
36022     }
36023     
36024 });
36025
36026  
36027
36028  /*
36029  * - LGPL
36030  *
36031  * Number field 
36032  */
36033
36034 /**
36035  * @class Roo.bootstrap.NumberField
36036  * @extends Roo.bootstrap.Input
36037  * Bootstrap NumberField class
36038  * 
36039  * 
36040  * 
36041  * 
36042  * @constructor
36043  * Create a new NumberField
36044  * @param {Object} config The config object
36045  */
36046
36047 Roo.bootstrap.NumberField = function(config){
36048     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36049 };
36050
36051 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36052     
36053     /**
36054      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36055      */
36056     allowDecimals : true,
36057     /**
36058      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36059      */
36060     decimalSeparator : ".",
36061     /**
36062      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36063      */
36064     decimalPrecision : 2,
36065     /**
36066      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36067      */
36068     allowNegative : true,
36069     
36070     /**
36071      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36072      */
36073     allowZero: true,
36074     /**
36075      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36076      */
36077     minValue : Number.NEGATIVE_INFINITY,
36078     /**
36079      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36080      */
36081     maxValue : Number.MAX_VALUE,
36082     /**
36083      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36084      */
36085     minText : "The minimum value for this field is {0}",
36086     /**
36087      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36088      */
36089     maxText : "The maximum value for this field is {0}",
36090     /**
36091      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36092      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36093      */
36094     nanText : "{0} is not a valid number",
36095     /**
36096      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36097      */
36098     thousandsDelimiter : false,
36099     /**
36100      * @cfg {String} valueAlign alignment of value
36101      */
36102     valueAlign : "left",
36103
36104     getAutoCreate : function()
36105     {
36106         var hiddenInput = {
36107             tag: 'input',
36108             type: 'hidden',
36109             id: Roo.id(),
36110             cls: 'hidden-number-input'
36111         };
36112         
36113         if (this.name) {
36114             hiddenInput.name = this.name;
36115         }
36116         
36117         this.name = '';
36118         
36119         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36120         
36121         this.name = hiddenInput.name;
36122         
36123         if(cfg.cn.length > 0) {
36124             cfg.cn.push(hiddenInput);
36125         }
36126         
36127         return cfg;
36128     },
36129
36130     // private
36131     initEvents : function()
36132     {   
36133         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36134         
36135         var allowed = "0123456789";
36136         
36137         if(this.allowDecimals){
36138             allowed += this.decimalSeparator;
36139         }
36140         
36141         if(this.allowNegative){
36142             allowed += "-";
36143         }
36144         
36145         if(this.thousandsDelimiter) {
36146             allowed += ",";
36147         }
36148         
36149         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36150         
36151         var keyPress = function(e){
36152             
36153             var k = e.getKey();
36154             
36155             var c = e.getCharCode();
36156             
36157             if(
36158                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36159                     allowed.indexOf(String.fromCharCode(c)) === -1
36160             ){
36161                 e.stopEvent();
36162                 return;
36163             }
36164             
36165             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36166                 return;
36167             }
36168             
36169             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36170                 e.stopEvent();
36171             }
36172         };
36173         
36174         this.el.on("keypress", keyPress, this);
36175     },
36176     
36177     validateValue : function(value)
36178     {
36179         
36180         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36181             return false;
36182         }
36183         
36184         var num = this.parseValue(value);
36185         
36186         if(isNaN(num)){
36187             this.markInvalid(String.format(this.nanText, value));
36188             return false;
36189         }
36190         
36191         if(num < this.minValue){
36192             this.markInvalid(String.format(this.minText, this.minValue));
36193             return false;
36194         }
36195         
36196         if(num > this.maxValue){
36197             this.markInvalid(String.format(this.maxText, this.maxValue));
36198             return false;
36199         }
36200         
36201         return true;
36202     },
36203
36204     getValue : function()
36205     {
36206         var v = this.hiddenEl().getValue();
36207         
36208         return this.fixPrecision(this.parseValue(v));
36209     },
36210
36211     parseValue : function(value)
36212     {
36213         if(this.thousandsDelimiter) {
36214             value += "";
36215             r = new RegExp(",", "g");
36216             value = value.replace(r, "");
36217         }
36218         
36219         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36220         return isNaN(value) ? '' : value;
36221     },
36222
36223     fixPrecision : function(value)
36224     {
36225         if(this.thousandsDelimiter) {
36226             value += "";
36227             r = new RegExp(",", "g");
36228             value = value.replace(r, "");
36229         }
36230         
36231         var nan = isNaN(value);
36232         
36233         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36234             return nan ? '' : value;
36235         }
36236         return parseFloat(value).toFixed(this.decimalPrecision);
36237     },
36238
36239     setValue : function(v)
36240     {
36241         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36242         
36243         this.value = v;
36244         
36245         if(this.rendered){
36246             
36247             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36248             
36249             this.inputEl().dom.value = (v == '') ? '' :
36250                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36251             
36252             if(!this.allowZero && v === '0') {
36253                 this.hiddenEl().dom.value = '';
36254                 this.inputEl().dom.value = '';
36255             }
36256             
36257             this.validate();
36258         }
36259     },
36260
36261     decimalPrecisionFcn : function(v)
36262     {
36263         return Math.floor(v);
36264     },
36265
36266     beforeBlur : function()
36267     {
36268         var v = this.parseValue(this.getRawValue());
36269         
36270         if(v || v === 0 || v === ''){
36271             this.setValue(v);
36272         }
36273     },
36274     
36275     hiddenEl : function()
36276     {
36277         return this.el.select('input.hidden-number-input',true).first();
36278     }
36279     
36280 });
36281
36282  
36283
36284 /*
36285 * Licence: LGPL
36286 */
36287
36288 /**
36289  * @class Roo.bootstrap.DocumentSlider
36290  * @extends Roo.bootstrap.Component
36291  * Bootstrap DocumentSlider class
36292  * 
36293  * @constructor
36294  * Create a new DocumentViewer
36295  * @param {Object} config The config object
36296  */
36297
36298 Roo.bootstrap.DocumentSlider = function(config){
36299     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36300     
36301     this.files = [];
36302     
36303     this.addEvents({
36304         /**
36305          * @event initial
36306          * Fire after initEvent
36307          * @param {Roo.bootstrap.DocumentSlider} this
36308          */
36309         "initial" : true,
36310         /**
36311          * @event update
36312          * Fire after update
36313          * @param {Roo.bootstrap.DocumentSlider} this
36314          */
36315         "update" : true,
36316         /**
36317          * @event click
36318          * Fire after click
36319          * @param {Roo.bootstrap.DocumentSlider} this
36320          */
36321         "click" : true
36322     });
36323 };
36324
36325 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36326     
36327     files : false,
36328     
36329     indicator : 0,
36330     
36331     getAutoCreate : function()
36332     {
36333         var cfg = {
36334             tag : 'div',
36335             cls : 'roo-document-slider',
36336             cn : [
36337                 {
36338                     tag : 'div',
36339                     cls : 'roo-document-slider-header',
36340                     cn : [
36341                         {
36342                             tag : 'div',
36343                             cls : 'roo-document-slider-header-title'
36344                         }
36345                     ]
36346                 },
36347                 {
36348                     tag : 'div',
36349                     cls : 'roo-document-slider-body',
36350                     cn : [
36351                         {
36352                             tag : 'div',
36353                             cls : 'roo-document-slider-prev',
36354                             cn : [
36355                                 {
36356                                     tag : 'i',
36357                                     cls : 'fa fa-chevron-left'
36358                                 }
36359                             ]
36360                         },
36361                         {
36362                             tag : 'div',
36363                             cls : 'roo-document-slider-thumb',
36364                             cn : [
36365                                 {
36366                                     tag : 'img',
36367                                     cls : 'roo-document-slider-image'
36368                                 }
36369                             ]
36370                         },
36371                         {
36372                             tag : 'div',
36373                             cls : 'roo-document-slider-next',
36374                             cn : [
36375                                 {
36376                                     tag : 'i',
36377                                     cls : 'fa fa-chevron-right'
36378                                 }
36379                             ]
36380                         }
36381                     ]
36382                 }
36383             ]
36384         };
36385         
36386         return cfg;
36387     },
36388     
36389     initEvents : function()
36390     {
36391         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36392         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36393         
36394         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36395         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36396         
36397         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36398         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36399         
36400         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36401         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36402         
36403         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36404         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36405         
36406         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36407         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36408         
36409         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36410         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36411         
36412         this.thumbEl.on('click', this.onClick, this);
36413         
36414         this.prevIndicator.on('click', this.prev, this);
36415         
36416         this.nextIndicator.on('click', this.next, this);
36417         
36418     },
36419     
36420     initial : function()
36421     {
36422         if(this.files.length){
36423             this.indicator = 1;
36424             this.update()
36425         }
36426         
36427         this.fireEvent('initial', this);
36428     },
36429     
36430     update : function()
36431     {
36432         this.imageEl.attr('src', this.files[this.indicator - 1]);
36433         
36434         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36435         
36436         this.prevIndicator.show();
36437         
36438         if(this.indicator == 1){
36439             this.prevIndicator.hide();
36440         }
36441         
36442         this.nextIndicator.show();
36443         
36444         if(this.indicator == this.files.length){
36445             this.nextIndicator.hide();
36446         }
36447         
36448         this.thumbEl.scrollTo('top');
36449         
36450         this.fireEvent('update', this);
36451     },
36452     
36453     onClick : function(e)
36454     {
36455         e.preventDefault();
36456         
36457         this.fireEvent('click', this);
36458     },
36459     
36460     prev : function(e)
36461     {
36462         e.preventDefault();
36463         
36464         this.indicator = Math.max(1, this.indicator - 1);
36465         
36466         this.update();
36467     },
36468     
36469     next : function(e)
36470     {
36471         e.preventDefault();
36472         
36473         this.indicator = Math.min(this.files.length, this.indicator + 1);
36474         
36475         this.update();
36476     }
36477 });
36478 /*
36479  * - LGPL
36480  *
36481  * RadioSet
36482  *
36483  *
36484  */
36485
36486 /**
36487  * @class Roo.bootstrap.RadioSet
36488  * @extends Roo.bootstrap.Input
36489  * Bootstrap RadioSet class
36490  * @cfg {String} indicatorpos (left|right) default left
36491  * @cfg {Boolean} inline (true|false) inline the element (default true)
36492  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36493  * @constructor
36494  * Create a new RadioSet
36495  * @param {Object} config The config object
36496  */
36497
36498 Roo.bootstrap.RadioSet = function(config){
36499     
36500     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36501     
36502     this.radioes = [];
36503     
36504     Roo.bootstrap.RadioSet.register(this);
36505     
36506     this.addEvents({
36507         /**
36508         * @event check
36509         * Fires when the element is checked or unchecked.
36510         * @param {Roo.bootstrap.RadioSet} this This radio
36511         * @param {Roo.bootstrap.Radio} item The checked item
36512         */
36513        check : true,
36514        /**
36515         * @event click
36516         * Fires when the element is click.
36517         * @param {Roo.bootstrap.RadioSet} this This radio set
36518         * @param {Roo.bootstrap.Radio} item The checked item
36519         * @param {Roo.EventObject} e The event object
36520         */
36521        click : true
36522     });
36523     
36524 };
36525
36526 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36527
36528     radioes : false,
36529     
36530     inline : true,
36531     
36532     weight : '',
36533     
36534     indicatorpos : 'left',
36535     
36536     getAutoCreate : function()
36537     {
36538         var label = {
36539             tag : 'label',
36540             cls : 'roo-radio-set-label',
36541             cn : [
36542                 {
36543                     tag : 'span',
36544                     html : this.fieldLabel
36545                 }
36546             ]
36547         };
36548         if (Roo.bootstrap.version == 3) {
36549             
36550             
36551             if(this.indicatorpos == 'left'){
36552                 label.cn.unshift({
36553                     tag : 'i',
36554                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36555                     tooltip : 'This field is required'
36556                 });
36557             } else {
36558                 label.cn.push({
36559                     tag : 'i',
36560                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36561                     tooltip : 'This field is required'
36562                 });
36563             }
36564         }
36565         var items = {
36566             tag : 'div',
36567             cls : 'roo-radio-set-items'
36568         };
36569         
36570         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36571         
36572         if (align === 'left' && this.fieldLabel.length) {
36573             
36574             items = {
36575                 cls : "roo-radio-set-right", 
36576                 cn: [
36577                     items
36578                 ]
36579             };
36580             
36581             if(this.labelWidth > 12){
36582                 label.style = "width: " + this.labelWidth + 'px';
36583             }
36584             
36585             if(this.labelWidth < 13 && this.labelmd == 0){
36586                 this.labelmd = this.labelWidth;
36587             }
36588             
36589             if(this.labellg > 0){
36590                 label.cls += ' col-lg-' + this.labellg;
36591                 items.cls += ' col-lg-' + (12 - this.labellg);
36592             }
36593             
36594             if(this.labelmd > 0){
36595                 label.cls += ' col-md-' + this.labelmd;
36596                 items.cls += ' col-md-' + (12 - this.labelmd);
36597             }
36598             
36599             if(this.labelsm > 0){
36600                 label.cls += ' col-sm-' + this.labelsm;
36601                 items.cls += ' col-sm-' + (12 - this.labelsm);
36602             }
36603             
36604             if(this.labelxs > 0){
36605                 label.cls += ' col-xs-' + this.labelxs;
36606                 items.cls += ' col-xs-' + (12 - this.labelxs);
36607             }
36608         }
36609         
36610         var cfg = {
36611             tag : 'div',
36612             cls : 'roo-radio-set',
36613             cn : [
36614                 {
36615                     tag : 'input',
36616                     cls : 'roo-radio-set-input',
36617                     type : 'hidden',
36618                     name : this.name,
36619                     value : this.value ? this.value :  ''
36620                 },
36621                 label,
36622                 items
36623             ]
36624         };
36625         
36626         if(this.weight.length){
36627             cfg.cls += ' roo-radio-' + this.weight;
36628         }
36629         
36630         if(this.inline) {
36631             cfg.cls += ' roo-radio-set-inline';
36632         }
36633         
36634         var settings=this;
36635         ['xs','sm','md','lg'].map(function(size){
36636             if (settings[size]) {
36637                 cfg.cls += ' col-' + size + '-' + settings[size];
36638             }
36639         });
36640         
36641         return cfg;
36642         
36643     },
36644
36645     initEvents : function()
36646     {
36647         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36648         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36649         
36650         if(!this.fieldLabel.length){
36651             this.labelEl.hide();
36652         }
36653         
36654         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36655         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36656         
36657         this.indicator = this.indicatorEl();
36658         
36659         if(this.indicator){
36660             this.indicator.addClass('invisible');
36661         }
36662         
36663         this.originalValue = this.getValue();
36664         
36665     },
36666     
36667     inputEl: function ()
36668     {
36669         return this.el.select('.roo-radio-set-input', true).first();
36670     },
36671     
36672     getChildContainer : function()
36673     {
36674         return this.itemsEl;
36675     },
36676     
36677     register : function(item)
36678     {
36679         this.radioes.push(item);
36680         
36681     },
36682     
36683     validate : function()
36684     {   
36685         if(this.getVisibilityEl().hasClass('hidden')){
36686             return true;
36687         }
36688         
36689         var valid = false;
36690         
36691         Roo.each(this.radioes, function(i){
36692             if(!i.checked){
36693                 return;
36694             }
36695             
36696             valid = true;
36697             return false;
36698         });
36699         
36700         if(this.allowBlank) {
36701             return true;
36702         }
36703         
36704         if(this.disabled || valid){
36705             this.markValid();
36706             return true;
36707         }
36708         
36709         this.markInvalid();
36710         return false;
36711         
36712     },
36713     
36714     markValid : function()
36715     {
36716         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36717             this.indicatorEl().removeClass('visible');
36718             this.indicatorEl().addClass('invisible');
36719         }
36720         
36721         
36722         if (Roo.bootstrap.version == 3) {
36723             this.el.removeClass([this.invalidClass, this.validClass]);
36724             this.el.addClass(this.validClass);
36725         } else {
36726             this.el.removeClass(['is-invalid','is-valid']);
36727             this.el.addClass(['is-valid']);
36728         }
36729         this.fireEvent('valid', this);
36730     },
36731     
36732     markInvalid : function(msg)
36733     {
36734         if(this.allowBlank || this.disabled){
36735             return;
36736         }
36737         
36738         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36739             this.indicatorEl().removeClass('invisible');
36740             this.indicatorEl().addClass('visible');
36741         }
36742         if (Roo.bootstrap.version == 3) {
36743             this.el.removeClass([this.invalidClass, this.validClass]);
36744             this.el.addClass(this.invalidClass);
36745         } else {
36746             this.el.removeClass(['is-invalid','is-valid']);
36747             this.el.addClass(['is-invalid']);
36748         }
36749         
36750         this.fireEvent('invalid', this, msg);
36751         
36752     },
36753     
36754     setValue : function(v, suppressEvent)
36755     {   
36756         if(this.value === v){
36757             return;
36758         }
36759         
36760         this.value = v;
36761         
36762         if(this.rendered){
36763             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36764         }
36765         
36766         Roo.each(this.radioes, function(i){
36767             i.checked = false;
36768             i.el.removeClass('checked');
36769         });
36770         
36771         Roo.each(this.radioes, function(i){
36772             
36773             if(i.value === v || i.value.toString() === v.toString()){
36774                 i.checked = true;
36775                 i.el.addClass('checked');
36776                 
36777                 if(suppressEvent !== true){
36778                     this.fireEvent('check', this, i);
36779                 }
36780                 
36781                 return false;
36782             }
36783             
36784         }, this);
36785         
36786         this.validate();
36787     },
36788     
36789     clearInvalid : function(){
36790         
36791         if(!this.el || this.preventMark){
36792             return;
36793         }
36794         
36795         this.el.removeClass([this.invalidClass]);
36796         
36797         this.fireEvent('valid', this);
36798     }
36799     
36800 });
36801
36802 Roo.apply(Roo.bootstrap.RadioSet, {
36803     
36804     groups: {},
36805     
36806     register : function(set)
36807     {
36808         this.groups[set.name] = set;
36809     },
36810     
36811     get: function(name) 
36812     {
36813         if (typeof(this.groups[name]) == 'undefined') {
36814             return false;
36815         }
36816         
36817         return this.groups[name] ;
36818     }
36819     
36820 });
36821 /*
36822  * Based on:
36823  * Ext JS Library 1.1.1
36824  * Copyright(c) 2006-2007, Ext JS, LLC.
36825  *
36826  * Originally Released Under LGPL - original licence link has changed is not relivant.
36827  *
36828  * Fork - LGPL
36829  * <script type="text/javascript">
36830  */
36831
36832
36833 /**
36834  * @class Roo.bootstrap.SplitBar
36835  * @extends Roo.util.Observable
36836  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36837  * <br><br>
36838  * Usage:
36839  * <pre><code>
36840 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36841                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36842 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36843 split.minSize = 100;
36844 split.maxSize = 600;
36845 split.animate = true;
36846 split.on('moved', splitterMoved);
36847 </code></pre>
36848  * @constructor
36849  * Create a new SplitBar
36850  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36851  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36852  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36853  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36854                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36855                         position of the SplitBar).
36856  */
36857 Roo.bootstrap.SplitBar = function(cfg){
36858     
36859     /** @private */
36860     
36861     //{
36862     //  dragElement : elm
36863     //  resizingElement: el,
36864         // optional..
36865     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36866     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36867         // existingProxy ???
36868     //}
36869     
36870     this.el = Roo.get(cfg.dragElement, true);
36871     this.el.dom.unselectable = "on";
36872     /** @private */
36873     this.resizingEl = Roo.get(cfg.resizingElement, true);
36874
36875     /**
36876      * @private
36877      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36878      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36879      * @type Number
36880      */
36881     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36882     
36883     /**
36884      * The minimum size of the resizing element. (Defaults to 0)
36885      * @type Number
36886      */
36887     this.minSize = 0;
36888     
36889     /**
36890      * The maximum size of the resizing element. (Defaults to 2000)
36891      * @type Number
36892      */
36893     this.maxSize = 2000;
36894     
36895     /**
36896      * Whether to animate the transition to the new size
36897      * @type Boolean
36898      */
36899     this.animate = false;
36900     
36901     /**
36902      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36903      * @type Boolean
36904      */
36905     this.useShim = false;
36906     
36907     /** @private */
36908     this.shim = null;
36909     
36910     if(!cfg.existingProxy){
36911         /** @private */
36912         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36913     }else{
36914         this.proxy = Roo.get(cfg.existingProxy).dom;
36915     }
36916     /** @private */
36917     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36918     
36919     /** @private */
36920     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36921     
36922     /** @private */
36923     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36924     
36925     /** @private */
36926     this.dragSpecs = {};
36927     
36928     /**
36929      * @private The adapter to use to positon and resize elements
36930      */
36931     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36932     this.adapter.init(this);
36933     
36934     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36935         /** @private */
36936         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36937         this.el.addClass("roo-splitbar-h");
36938     }else{
36939         /** @private */
36940         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36941         this.el.addClass("roo-splitbar-v");
36942     }
36943     
36944     this.addEvents({
36945         /**
36946          * @event resize
36947          * Fires when the splitter is moved (alias for {@link #event-moved})
36948          * @param {Roo.bootstrap.SplitBar} this
36949          * @param {Number} newSize the new width or height
36950          */
36951         "resize" : true,
36952         /**
36953          * @event moved
36954          * Fires when the splitter is moved
36955          * @param {Roo.bootstrap.SplitBar} this
36956          * @param {Number} newSize the new width or height
36957          */
36958         "moved" : true,
36959         /**
36960          * @event beforeresize
36961          * Fires before the splitter is dragged
36962          * @param {Roo.bootstrap.SplitBar} this
36963          */
36964         "beforeresize" : true,
36965
36966         "beforeapply" : true
36967     });
36968
36969     Roo.util.Observable.call(this);
36970 };
36971
36972 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36973     onStartProxyDrag : function(x, y){
36974         this.fireEvent("beforeresize", this);
36975         if(!this.overlay){
36976             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36977             o.unselectable();
36978             o.enableDisplayMode("block");
36979             // all splitbars share the same overlay
36980             Roo.bootstrap.SplitBar.prototype.overlay = o;
36981         }
36982         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36983         this.overlay.show();
36984         Roo.get(this.proxy).setDisplayed("block");
36985         var size = this.adapter.getElementSize(this);
36986         this.activeMinSize = this.getMinimumSize();;
36987         this.activeMaxSize = this.getMaximumSize();;
36988         var c1 = size - this.activeMinSize;
36989         var c2 = Math.max(this.activeMaxSize - size, 0);
36990         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36991             this.dd.resetConstraints();
36992             this.dd.setXConstraint(
36993                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36994                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36995             );
36996             this.dd.setYConstraint(0, 0);
36997         }else{
36998             this.dd.resetConstraints();
36999             this.dd.setXConstraint(0, 0);
37000             this.dd.setYConstraint(
37001                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37002                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37003             );
37004          }
37005         this.dragSpecs.startSize = size;
37006         this.dragSpecs.startPoint = [x, y];
37007         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37008     },
37009     
37010     /** 
37011      * @private Called after the drag operation by the DDProxy
37012      */
37013     onEndProxyDrag : function(e){
37014         Roo.get(this.proxy).setDisplayed(false);
37015         var endPoint = Roo.lib.Event.getXY(e);
37016         if(this.overlay){
37017             this.overlay.hide();
37018         }
37019         var newSize;
37020         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37021             newSize = this.dragSpecs.startSize + 
37022                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37023                     endPoint[0] - this.dragSpecs.startPoint[0] :
37024                     this.dragSpecs.startPoint[0] - endPoint[0]
37025                 );
37026         }else{
37027             newSize = this.dragSpecs.startSize + 
37028                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37029                     endPoint[1] - this.dragSpecs.startPoint[1] :
37030                     this.dragSpecs.startPoint[1] - endPoint[1]
37031                 );
37032         }
37033         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37034         if(newSize != this.dragSpecs.startSize){
37035             if(this.fireEvent('beforeapply', this, newSize) !== false){
37036                 this.adapter.setElementSize(this, newSize);
37037                 this.fireEvent("moved", this, newSize);
37038                 this.fireEvent("resize", this, newSize);
37039             }
37040         }
37041     },
37042     
37043     /**
37044      * Get the adapter this SplitBar uses
37045      * @return The adapter object
37046      */
37047     getAdapter : function(){
37048         return this.adapter;
37049     },
37050     
37051     /**
37052      * Set the adapter this SplitBar uses
37053      * @param {Object} adapter A SplitBar adapter object
37054      */
37055     setAdapter : function(adapter){
37056         this.adapter = adapter;
37057         this.adapter.init(this);
37058     },
37059     
37060     /**
37061      * Gets the minimum size for the resizing element
37062      * @return {Number} The minimum size
37063      */
37064     getMinimumSize : function(){
37065         return this.minSize;
37066     },
37067     
37068     /**
37069      * Sets the minimum size for the resizing element
37070      * @param {Number} minSize The minimum size
37071      */
37072     setMinimumSize : function(minSize){
37073         this.minSize = minSize;
37074     },
37075     
37076     /**
37077      * Gets the maximum size for the resizing element
37078      * @return {Number} The maximum size
37079      */
37080     getMaximumSize : function(){
37081         return this.maxSize;
37082     },
37083     
37084     /**
37085      * Sets the maximum size for the resizing element
37086      * @param {Number} maxSize The maximum size
37087      */
37088     setMaximumSize : function(maxSize){
37089         this.maxSize = maxSize;
37090     },
37091     
37092     /**
37093      * Sets the initialize size for the resizing element
37094      * @param {Number} size The initial size
37095      */
37096     setCurrentSize : function(size){
37097         var oldAnimate = this.animate;
37098         this.animate = false;
37099         this.adapter.setElementSize(this, size);
37100         this.animate = oldAnimate;
37101     },
37102     
37103     /**
37104      * Destroy this splitbar. 
37105      * @param {Boolean} removeEl True to remove the element
37106      */
37107     destroy : function(removeEl){
37108         if(this.shim){
37109             this.shim.remove();
37110         }
37111         this.dd.unreg();
37112         this.proxy.parentNode.removeChild(this.proxy);
37113         if(removeEl){
37114             this.el.remove();
37115         }
37116     }
37117 });
37118
37119 /**
37120  * @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.
37121  */
37122 Roo.bootstrap.SplitBar.createProxy = function(dir){
37123     var proxy = new Roo.Element(document.createElement("div"));
37124     proxy.unselectable();
37125     var cls = 'roo-splitbar-proxy';
37126     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37127     document.body.appendChild(proxy.dom);
37128     return proxy.dom;
37129 };
37130
37131 /** 
37132  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37133  * Default Adapter. It assumes the splitter and resizing element are not positioned
37134  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37135  */
37136 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37137 };
37138
37139 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37140     // do nothing for now
37141     init : function(s){
37142     
37143     },
37144     /**
37145      * Called before drag operations to get the current size of the resizing element. 
37146      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37147      */
37148      getElementSize : function(s){
37149         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37150             return s.resizingEl.getWidth();
37151         }else{
37152             return s.resizingEl.getHeight();
37153         }
37154     },
37155     
37156     /**
37157      * Called after drag operations to set the size of the resizing element.
37158      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37159      * @param {Number} newSize The new size to set
37160      * @param {Function} onComplete A function to be invoked when resizing is complete
37161      */
37162     setElementSize : function(s, newSize, onComplete){
37163         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37164             if(!s.animate){
37165                 s.resizingEl.setWidth(newSize);
37166                 if(onComplete){
37167                     onComplete(s, newSize);
37168                 }
37169             }else{
37170                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37171             }
37172         }else{
37173             
37174             if(!s.animate){
37175                 s.resizingEl.setHeight(newSize);
37176                 if(onComplete){
37177                     onComplete(s, newSize);
37178                 }
37179             }else{
37180                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37181             }
37182         }
37183     }
37184 };
37185
37186 /** 
37187  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37188  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37189  * Adapter that  moves the splitter element to align with the resized sizing element. 
37190  * Used with an absolute positioned SplitBar.
37191  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37192  * document.body, make sure you assign an id to the body element.
37193  */
37194 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37195     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37196     this.container = Roo.get(container);
37197 };
37198
37199 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37200     init : function(s){
37201         this.basic.init(s);
37202     },
37203     
37204     getElementSize : function(s){
37205         return this.basic.getElementSize(s);
37206     },
37207     
37208     setElementSize : function(s, newSize, onComplete){
37209         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37210     },
37211     
37212     moveSplitter : function(s){
37213         var yes = Roo.bootstrap.SplitBar;
37214         switch(s.placement){
37215             case yes.LEFT:
37216                 s.el.setX(s.resizingEl.getRight());
37217                 break;
37218             case yes.RIGHT:
37219                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37220                 break;
37221             case yes.TOP:
37222                 s.el.setY(s.resizingEl.getBottom());
37223                 break;
37224             case yes.BOTTOM:
37225                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37226                 break;
37227         }
37228     }
37229 };
37230
37231 /**
37232  * Orientation constant - Create a vertical SplitBar
37233  * @static
37234  * @type Number
37235  */
37236 Roo.bootstrap.SplitBar.VERTICAL = 1;
37237
37238 /**
37239  * Orientation constant - Create a horizontal SplitBar
37240  * @static
37241  * @type Number
37242  */
37243 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37244
37245 /**
37246  * Placement constant - The resizing element is to the left of the splitter element
37247  * @static
37248  * @type Number
37249  */
37250 Roo.bootstrap.SplitBar.LEFT = 1;
37251
37252 /**
37253  * Placement constant - The resizing element is to the right of the splitter element
37254  * @static
37255  * @type Number
37256  */
37257 Roo.bootstrap.SplitBar.RIGHT = 2;
37258
37259 /**
37260  * Placement constant - The resizing element is positioned above the splitter element
37261  * @static
37262  * @type Number
37263  */
37264 Roo.bootstrap.SplitBar.TOP = 3;
37265
37266 /**
37267  * Placement constant - The resizing element is positioned under splitter element
37268  * @static
37269  * @type Number
37270  */
37271 Roo.bootstrap.SplitBar.BOTTOM = 4;
37272 Roo.namespace("Roo.bootstrap.layout");/*
37273  * Based on:
37274  * Ext JS Library 1.1.1
37275  * Copyright(c) 2006-2007, Ext JS, LLC.
37276  *
37277  * Originally Released Under LGPL - original licence link has changed is not relivant.
37278  *
37279  * Fork - LGPL
37280  * <script type="text/javascript">
37281  */
37282
37283 /**
37284  * @class Roo.bootstrap.layout.Manager
37285  * @extends Roo.bootstrap.Component
37286  * Base class for layout managers.
37287  */
37288 Roo.bootstrap.layout.Manager = function(config)
37289 {
37290     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37291
37292
37293
37294
37295
37296     /** false to disable window resize monitoring @type Boolean */
37297     this.monitorWindowResize = true;
37298     this.regions = {};
37299     this.addEvents({
37300         /**
37301          * @event layout
37302          * Fires when a layout is performed.
37303          * @param {Roo.LayoutManager} this
37304          */
37305         "layout" : true,
37306         /**
37307          * @event regionresized
37308          * Fires when the user resizes a region.
37309          * @param {Roo.LayoutRegion} region The resized region
37310          * @param {Number} newSize The new size (width for east/west, height for north/south)
37311          */
37312         "regionresized" : true,
37313         /**
37314          * @event regioncollapsed
37315          * Fires when a region is collapsed.
37316          * @param {Roo.LayoutRegion} region The collapsed region
37317          */
37318         "regioncollapsed" : true,
37319         /**
37320          * @event regionexpanded
37321          * Fires when a region is expanded.
37322          * @param {Roo.LayoutRegion} region The expanded region
37323          */
37324         "regionexpanded" : true
37325     });
37326     this.updating = false;
37327
37328     if (config.el) {
37329         this.el = Roo.get(config.el);
37330         this.initEvents();
37331     }
37332
37333 };
37334
37335 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37336
37337
37338     regions : null,
37339
37340     monitorWindowResize : true,
37341
37342
37343     updating : false,
37344
37345
37346     onRender : function(ct, position)
37347     {
37348         if(!this.el){
37349             this.el = Roo.get(ct);
37350             this.initEvents();
37351         }
37352         //this.fireEvent('render',this);
37353     },
37354
37355
37356     initEvents: function()
37357     {
37358
37359
37360         // ie scrollbar fix
37361         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37362             document.body.scroll = "no";
37363         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37364             this.el.position('relative');
37365         }
37366         this.id = this.el.id;
37367         this.el.addClass("roo-layout-container");
37368         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37369         if(this.el.dom != document.body ) {
37370             this.el.on('resize', this.layout,this);
37371             this.el.on('show', this.layout,this);
37372         }
37373
37374     },
37375
37376     /**
37377      * Returns true if this layout is currently being updated
37378      * @return {Boolean}
37379      */
37380     isUpdating : function(){
37381         return this.updating;
37382     },
37383
37384     /**
37385      * Suspend the LayoutManager from doing auto-layouts while
37386      * making multiple add or remove calls
37387      */
37388     beginUpdate : function(){
37389         this.updating = true;
37390     },
37391
37392     /**
37393      * Restore auto-layouts and optionally disable the manager from performing a layout
37394      * @param {Boolean} noLayout true to disable a layout update
37395      */
37396     endUpdate : function(noLayout){
37397         this.updating = false;
37398         if(!noLayout){
37399             this.layout();
37400         }
37401     },
37402
37403     layout: function(){
37404         // abstract...
37405     },
37406
37407     onRegionResized : function(region, newSize){
37408         this.fireEvent("regionresized", region, newSize);
37409         this.layout();
37410     },
37411
37412     onRegionCollapsed : function(region){
37413         this.fireEvent("regioncollapsed", region);
37414     },
37415
37416     onRegionExpanded : function(region){
37417         this.fireEvent("regionexpanded", region);
37418     },
37419
37420     /**
37421      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37422      * performs box-model adjustments.
37423      * @return {Object} The size as an object {width: (the width), height: (the height)}
37424      */
37425     getViewSize : function()
37426     {
37427         var size;
37428         if(this.el.dom != document.body){
37429             size = this.el.getSize();
37430         }else{
37431             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37432         }
37433         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37434         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37435         return size;
37436     },
37437
37438     /**
37439      * Returns the Element this layout is bound to.
37440      * @return {Roo.Element}
37441      */
37442     getEl : function(){
37443         return this.el;
37444     },
37445
37446     /**
37447      * Returns the specified region.
37448      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37449      * @return {Roo.LayoutRegion}
37450      */
37451     getRegion : function(target){
37452         return this.regions[target.toLowerCase()];
37453     },
37454
37455     onWindowResize : function(){
37456         if(this.monitorWindowResize){
37457             this.layout();
37458         }
37459     }
37460 });
37461 /*
37462  * Based on:
37463  * Ext JS Library 1.1.1
37464  * Copyright(c) 2006-2007, Ext JS, LLC.
37465  *
37466  * Originally Released Under LGPL - original licence link has changed is not relivant.
37467  *
37468  * Fork - LGPL
37469  * <script type="text/javascript">
37470  */
37471 /**
37472  * @class Roo.bootstrap.layout.Border
37473  * @extends Roo.bootstrap.layout.Manager
37474  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37475  * please see: examples/bootstrap/nested.html<br><br>
37476  
37477 <b>The container the layout is rendered into can be either the body element or any other element.
37478 If it is not the body element, the container needs to either be an absolute positioned element,
37479 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37480 the container size if it is not the body element.</b>
37481
37482 * @constructor
37483 * Create a new Border
37484 * @param {Object} config Configuration options
37485  */
37486 Roo.bootstrap.layout.Border = function(config){
37487     config = config || {};
37488     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37489     
37490     
37491     
37492     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37493         if(config[region]){
37494             config[region].region = region;
37495             this.addRegion(config[region]);
37496         }
37497     },this);
37498     
37499 };
37500
37501 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37502
37503 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37504     
37505     parent : false, // this might point to a 'nest' or a ???
37506     
37507     /**
37508      * Creates and adds a new region if it doesn't already exist.
37509      * @param {String} target The target region key (north, south, east, west or center).
37510      * @param {Object} config The regions config object
37511      * @return {BorderLayoutRegion} The new region
37512      */
37513     addRegion : function(config)
37514     {
37515         if(!this.regions[config.region]){
37516             var r = this.factory(config);
37517             this.bindRegion(r);
37518         }
37519         return this.regions[config.region];
37520     },
37521
37522     // private (kinda)
37523     bindRegion : function(r){
37524         this.regions[r.config.region] = r;
37525         
37526         r.on("visibilitychange",    this.layout, this);
37527         r.on("paneladded",          this.layout, this);
37528         r.on("panelremoved",        this.layout, this);
37529         r.on("invalidated",         this.layout, this);
37530         r.on("resized",             this.onRegionResized, this);
37531         r.on("collapsed",           this.onRegionCollapsed, this);
37532         r.on("expanded",            this.onRegionExpanded, this);
37533     },
37534
37535     /**
37536      * Performs a layout update.
37537      */
37538     layout : function()
37539     {
37540         if(this.updating) {
37541             return;
37542         }
37543         
37544         // render all the rebions if they have not been done alreayd?
37545         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37546             if(this.regions[region] && !this.regions[region].bodyEl){
37547                 this.regions[region].onRender(this.el)
37548             }
37549         },this);
37550         
37551         var size = this.getViewSize();
37552         var w = size.width;
37553         var h = size.height;
37554         var centerW = w;
37555         var centerH = h;
37556         var centerY = 0;
37557         var centerX = 0;
37558         //var x = 0, y = 0;
37559
37560         var rs = this.regions;
37561         var north = rs["north"];
37562         var south = rs["south"]; 
37563         var west = rs["west"];
37564         var east = rs["east"];
37565         var center = rs["center"];
37566         //if(this.hideOnLayout){ // not supported anymore
37567             //c.el.setStyle("display", "none");
37568         //}
37569         if(north && north.isVisible()){
37570             var b = north.getBox();
37571             var m = north.getMargins();
37572             b.width = w - (m.left+m.right);
37573             b.x = m.left;
37574             b.y = m.top;
37575             centerY = b.height + b.y + m.bottom;
37576             centerH -= centerY;
37577             north.updateBox(this.safeBox(b));
37578         }
37579         if(south && south.isVisible()){
37580             var b = south.getBox();
37581             var m = south.getMargins();
37582             b.width = w - (m.left+m.right);
37583             b.x = m.left;
37584             var totalHeight = (b.height + m.top + m.bottom);
37585             b.y = h - totalHeight + m.top;
37586             centerH -= totalHeight;
37587             south.updateBox(this.safeBox(b));
37588         }
37589         if(west && west.isVisible()){
37590             var b = west.getBox();
37591             var m = west.getMargins();
37592             b.height = centerH - (m.top+m.bottom);
37593             b.x = m.left;
37594             b.y = centerY + m.top;
37595             var totalWidth = (b.width + m.left + m.right);
37596             centerX += totalWidth;
37597             centerW -= totalWidth;
37598             west.updateBox(this.safeBox(b));
37599         }
37600         if(east && east.isVisible()){
37601             var b = east.getBox();
37602             var m = east.getMargins();
37603             b.height = centerH - (m.top+m.bottom);
37604             var totalWidth = (b.width + m.left + m.right);
37605             b.x = w - totalWidth + m.left;
37606             b.y = centerY + m.top;
37607             centerW -= totalWidth;
37608             east.updateBox(this.safeBox(b));
37609         }
37610         if(center){
37611             var m = center.getMargins();
37612             var centerBox = {
37613                 x: centerX + m.left,
37614                 y: centerY + m.top,
37615                 width: centerW - (m.left+m.right),
37616                 height: centerH - (m.top+m.bottom)
37617             };
37618             //if(this.hideOnLayout){
37619                 //center.el.setStyle("display", "block");
37620             //}
37621             center.updateBox(this.safeBox(centerBox));
37622         }
37623         this.el.repaint();
37624         this.fireEvent("layout", this);
37625     },
37626
37627     // private
37628     safeBox : function(box){
37629         box.width = Math.max(0, box.width);
37630         box.height = Math.max(0, box.height);
37631         return box;
37632     },
37633
37634     /**
37635      * Adds a ContentPanel (or subclass) to this layout.
37636      * @param {String} target The target region key (north, south, east, west or center).
37637      * @param {Roo.ContentPanel} panel The panel to add
37638      * @return {Roo.ContentPanel} The added panel
37639      */
37640     add : function(target, panel){
37641          
37642         target = target.toLowerCase();
37643         return this.regions[target].add(panel);
37644     },
37645
37646     /**
37647      * Remove a ContentPanel (or subclass) to this layout.
37648      * @param {String} target The target region key (north, south, east, west or center).
37649      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37650      * @return {Roo.ContentPanel} The removed panel
37651      */
37652     remove : function(target, panel){
37653         target = target.toLowerCase();
37654         return this.regions[target].remove(panel);
37655     },
37656
37657     /**
37658      * Searches all regions for a panel with the specified id
37659      * @param {String} panelId
37660      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37661      */
37662     findPanel : function(panelId){
37663         var rs = this.regions;
37664         for(var target in rs){
37665             if(typeof rs[target] != "function"){
37666                 var p = rs[target].getPanel(panelId);
37667                 if(p){
37668                     return p;
37669                 }
37670             }
37671         }
37672         return null;
37673     },
37674
37675     /**
37676      * Searches all regions for a panel with the specified id and activates (shows) it.
37677      * @param {String/ContentPanel} panelId The panels id or the panel itself
37678      * @return {Roo.ContentPanel} The shown panel or null
37679      */
37680     showPanel : function(panelId) {
37681       var rs = this.regions;
37682       for(var target in rs){
37683          var r = rs[target];
37684          if(typeof r != "function"){
37685             if(r.hasPanel(panelId)){
37686                return r.showPanel(panelId);
37687             }
37688          }
37689       }
37690       return null;
37691    },
37692
37693    /**
37694      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37695      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37696      */
37697    /*
37698     restoreState : function(provider){
37699         if(!provider){
37700             provider = Roo.state.Manager;
37701         }
37702         var sm = new Roo.LayoutStateManager();
37703         sm.init(this, provider);
37704     },
37705 */
37706  
37707  
37708     /**
37709      * Adds a xtype elements to the layout.
37710      * <pre><code>
37711
37712 layout.addxtype({
37713        xtype : 'ContentPanel',
37714        region: 'west',
37715        items: [ .... ]
37716    }
37717 );
37718
37719 layout.addxtype({
37720         xtype : 'NestedLayoutPanel',
37721         region: 'west',
37722         layout: {
37723            center: { },
37724            west: { }   
37725         },
37726         items : [ ... list of content panels or nested layout panels.. ]
37727    }
37728 );
37729 </code></pre>
37730      * @param {Object} cfg Xtype definition of item to add.
37731      */
37732     addxtype : function(cfg)
37733     {
37734         // basically accepts a pannel...
37735         // can accept a layout region..!?!?
37736         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37737         
37738         
37739         // theory?  children can only be panels??
37740         
37741         //if (!cfg.xtype.match(/Panel$/)) {
37742         //    return false;
37743         //}
37744         var ret = false;
37745         
37746         if (typeof(cfg.region) == 'undefined') {
37747             Roo.log("Failed to add Panel, region was not set");
37748             Roo.log(cfg);
37749             return false;
37750         }
37751         var region = cfg.region;
37752         delete cfg.region;
37753         
37754           
37755         var xitems = [];
37756         if (cfg.items) {
37757             xitems = cfg.items;
37758             delete cfg.items;
37759         }
37760         var nb = false;
37761         
37762         if ( region == 'center') {
37763             Roo.log("Center: " + cfg.title);
37764         }
37765         
37766         
37767         switch(cfg.xtype) 
37768         {
37769             case 'Content':  // ContentPanel (el, cfg)
37770             case 'Scroll':  // ContentPanel (el, cfg)
37771             case 'View': 
37772                 cfg.autoCreate = cfg.autoCreate || true;
37773                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37774                 //} else {
37775                 //    var el = this.el.createChild();
37776                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37777                 //}
37778                 
37779                 this.add(region, ret);
37780                 break;
37781             
37782             /*
37783             case 'TreePanel': // our new panel!
37784                 cfg.el = this.el.createChild();
37785                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37786                 this.add(region, ret);
37787                 break;
37788             */
37789             
37790             case 'Nest': 
37791                 // create a new Layout (which is  a Border Layout...
37792                 
37793                 var clayout = cfg.layout;
37794                 clayout.el  = this.el.createChild();
37795                 clayout.items   = clayout.items  || [];
37796                 
37797                 delete cfg.layout;
37798                 
37799                 // replace this exitems with the clayout ones..
37800                 xitems = clayout.items;
37801                  
37802                 // force background off if it's in center...
37803                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37804                     cfg.background = false;
37805                 }
37806                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37807                 
37808                 
37809                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37810                 //console.log('adding nested layout panel '  + cfg.toSource());
37811                 this.add(region, ret);
37812                 nb = {}; /// find first...
37813                 break;
37814             
37815             case 'Grid':
37816                 
37817                 // needs grid and region
37818                 
37819                 //var el = this.getRegion(region).el.createChild();
37820                 /*
37821                  *var el = this.el.createChild();
37822                 // create the grid first...
37823                 cfg.grid.container = el;
37824                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37825                 */
37826                 
37827                 if (region == 'center' && this.active ) {
37828                     cfg.background = false;
37829                 }
37830                 
37831                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37832                 
37833                 this.add(region, ret);
37834                 /*
37835                 if (cfg.background) {
37836                     // render grid on panel activation (if panel background)
37837                     ret.on('activate', function(gp) {
37838                         if (!gp.grid.rendered) {
37839                     //        gp.grid.render(el);
37840                         }
37841                     });
37842                 } else {
37843                   //  cfg.grid.render(el);
37844                 }
37845                 */
37846                 break;
37847            
37848            
37849             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37850                 // it was the old xcomponent building that caused this before.
37851                 // espeically if border is the top element in the tree.
37852                 ret = this;
37853                 break; 
37854                 
37855                     
37856                 
37857                 
37858                 
37859             default:
37860                 /*
37861                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37862                     
37863                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37864                     this.add(region, ret);
37865                 } else {
37866                 */
37867                     Roo.log(cfg);
37868                     throw "Can not add '" + cfg.xtype + "' to Border";
37869                     return null;
37870              
37871                                 
37872              
37873         }
37874         this.beginUpdate();
37875         // add children..
37876         var region = '';
37877         var abn = {};
37878         Roo.each(xitems, function(i)  {
37879             region = nb && i.region ? i.region : false;
37880             
37881             var add = ret.addxtype(i);
37882            
37883             if (region) {
37884                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37885                 if (!i.background) {
37886                     abn[region] = nb[region] ;
37887                 }
37888             }
37889             
37890         });
37891         this.endUpdate();
37892
37893         // make the last non-background panel active..
37894         //if (nb) { Roo.log(abn); }
37895         if (nb) {
37896             
37897             for(var r in abn) {
37898                 region = this.getRegion(r);
37899                 if (region) {
37900                     // tried using nb[r], but it does not work..
37901                      
37902                     region.showPanel(abn[r]);
37903                    
37904                 }
37905             }
37906         }
37907         return ret;
37908         
37909     },
37910     
37911     
37912 // private
37913     factory : function(cfg)
37914     {
37915         
37916         var validRegions = Roo.bootstrap.layout.Border.regions;
37917
37918         var target = cfg.region;
37919         cfg.mgr = this;
37920         
37921         var r = Roo.bootstrap.layout;
37922         Roo.log(target);
37923         switch(target){
37924             case "north":
37925                 return new r.North(cfg);
37926             case "south":
37927                 return new r.South(cfg);
37928             case "east":
37929                 return new r.East(cfg);
37930             case "west":
37931                 return new r.West(cfg);
37932             case "center":
37933                 return new r.Center(cfg);
37934         }
37935         throw 'Layout region "'+target+'" not supported.';
37936     }
37937     
37938     
37939 });
37940  /*
37941  * Based on:
37942  * Ext JS Library 1.1.1
37943  * Copyright(c) 2006-2007, Ext JS, LLC.
37944  *
37945  * Originally Released Under LGPL - original licence link has changed is not relivant.
37946  *
37947  * Fork - LGPL
37948  * <script type="text/javascript">
37949  */
37950  
37951 /**
37952  * @class Roo.bootstrap.layout.Basic
37953  * @extends Roo.util.Observable
37954  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37955  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37956  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37957  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37958  * @cfg {string}   region  the region that it inhabits..
37959  * @cfg {bool}   skipConfig skip config?
37960  * 
37961
37962  */
37963 Roo.bootstrap.layout.Basic = function(config){
37964     
37965     this.mgr = config.mgr;
37966     
37967     this.position = config.region;
37968     
37969     var skipConfig = config.skipConfig;
37970     
37971     this.events = {
37972         /**
37973          * @scope Roo.BasicLayoutRegion
37974          */
37975         
37976         /**
37977          * @event beforeremove
37978          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37979          * @param {Roo.LayoutRegion} this
37980          * @param {Roo.ContentPanel} panel The panel
37981          * @param {Object} e The cancel event object
37982          */
37983         "beforeremove" : true,
37984         /**
37985          * @event invalidated
37986          * Fires when the layout for this region is changed.
37987          * @param {Roo.LayoutRegion} this
37988          */
37989         "invalidated" : true,
37990         /**
37991          * @event visibilitychange
37992          * Fires when this region is shown or hidden 
37993          * @param {Roo.LayoutRegion} this
37994          * @param {Boolean} visibility true or false
37995          */
37996         "visibilitychange" : true,
37997         /**
37998          * @event paneladded
37999          * Fires when a panel is added. 
38000          * @param {Roo.LayoutRegion} this
38001          * @param {Roo.ContentPanel} panel The panel
38002          */
38003         "paneladded" : true,
38004         /**
38005          * @event panelremoved
38006          * Fires when a panel is removed. 
38007          * @param {Roo.LayoutRegion} this
38008          * @param {Roo.ContentPanel} panel The panel
38009          */
38010         "panelremoved" : true,
38011         /**
38012          * @event beforecollapse
38013          * Fires when this region before collapse.
38014          * @param {Roo.LayoutRegion} this
38015          */
38016         "beforecollapse" : true,
38017         /**
38018          * @event collapsed
38019          * Fires when this region is collapsed.
38020          * @param {Roo.LayoutRegion} this
38021          */
38022         "collapsed" : true,
38023         /**
38024          * @event expanded
38025          * Fires when this region is expanded.
38026          * @param {Roo.LayoutRegion} this
38027          */
38028         "expanded" : true,
38029         /**
38030          * @event slideshow
38031          * Fires when this region is slid into view.
38032          * @param {Roo.LayoutRegion} this
38033          */
38034         "slideshow" : true,
38035         /**
38036          * @event slidehide
38037          * Fires when this region slides out of view. 
38038          * @param {Roo.LayoutRegion} this
38039          */
38040         "slidehide" : true,
38041         /**
38042          * @event panelactivated
38043          * Fires when a panel is activated. 
38044          * @param {Roo.LayoutRegion} this
38045          * @param {Roo.ContentPanel} panel The activated panel
38046          */
38047         "panelactivated" : true,
38048         /**
38049          * @event resized
38050          * Fires when the user resizes this region. 
38051          * @param {Roo.LayoutRegion} this
38052          * @param {Number} newSize The new size (width for east/west, height for north/south)
38053          */
38054         "resized" : true
38055     };
38056     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38057     this.panels = new Roo.util.MixedCollection();
38058     this.panels.getKey = this.getPanelId.createDelegate(this);
38059     this.box = null;
38060     this.activePanel = null;
38061     // ensure listeners are added...
38062     
38063     if (config.listeners || config.events) {
38064         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38065             listeners : config.listeners || {},
38066             events : config.events || {}
38067         });
38068     }
38069     
38070     if(skipConfig !== true){
38071         this.applyConfig(config);
38072     }
38073 };
38074
38075 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38076 {
38077     getPanelId : function(p){
38078         return p.getId();
38079     },
38080     
38081     applyConfig : function(config){
38082         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38083         this.config = config;
38084         
38085     },
38086     
38087     /**
38088      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38089      * the width, for horizontal (north, south) the height.
38090      * @param {Number} newSize The new width or height
38091      */
38092     resizeTo : function(newSize){
38093         var el = this.el ? this.el :
38094                  (this.activePanel ? this.activePanel.getEl() : null);
38095         if(el){
38096             switch(this.position){
38097                 case "east":
38098                 case "west":
38099                     el.setWidth(newSize);
38100                     this.fireEvent("resized", this, newSize);
38101                 break;
38102                 case "north":
38103                 case "south":
38104                     el.setHeight(newSize);
38105                     this.fireEvent("resized", this, newSize);
38106                 break;                
38107             }
38108         }
38109     },
38110     
38111     getBox : function(){
38112         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38113     },
38114     
38115     getMargins : function(){
38116         return this.margins;
38117     },
38118     
38119     updateBox : function(box){
38120         this.box = box;
38121         var el = this.activePanel.getEl();
38122         el.dom.style.left = box.x + "px";
38123         el.dom.style.top = box.y + "px";
38124         this.activePanel.setSize(box.width, box.height);
38125     },
38126     
38127     /**
38128      * Returns the container element for this region.
38129      * @return {Roo.Element}
38130      */
38131     getEl : function(){
38132         return this.activePanel;
38133     },
38134     
38135     /**
38136      * Returns true if this region is currently visible.
38137      * @return {Boolean}
38138      */
38139     isVisible : function(){
38140         return this.activePanel ? true : false;
38141     },
38142     
38143     setActivePanel : function(panel){
38144         panel = this.getPanel(panel);
38145         if(this.activePanel && this.activePanel != panel){
38146             this.activePanel.setActiveState(false);
38147             this.activePanel.getEl().setLeftTop(-10000,-10000);
38148         }
38149         this.activePanel = panel;
38150         panel.setActiveState(true);
38151         if(this.box){
38152             panel.setSize(this.box.width, this.box.height);
38153         }
38154         this.fireEvent("panelactivated", this, panel);
38155         this.fireEvent("invalidated");
38156     },
38157     
38158     /**
38159      * Show the specified panel.
38160      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38161      * @return {Roo.ContentPanel} The shown panel or null
38162      */
38163     showPanel : function(panel){
38164         panel = this.getPanel(panel);
38165         if(panel){
38166             this.setActivePanel(panel);
38167         }
38168         return panel;
38169     },
38170     
38171     /**
38172      * Get the active panel for this region.
38173      * @return {Roo.ContentPanel} The active panel or null
38174      */
38175     getActivePanel : function(){
38176         return this.activePanel;
38177     },
38178     
38179     /**
38180      * Add the passed ContentPanel(s)
38181      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38182      * @return {Roo.ContentPanel} The panel added (if only one was added)
38183      */
38184     add : function(panel){
38185         if(arguments.length > 1){
38186             for(var i = 0, len = arguments.length; i < len; i++) {
38187                 this.add(arguments[i]);
38188             }
38189             return null;
38190         }
38191         if(this.hasPanel(panel)){
38192             this.showPanel(panel);
38193             return panel;
38194         }
38195         var el = panel.getEl();
38196         if(el.dom.parentNode != this.mgr.el.dom){
38197             this.mgr.el.dom.appendChild(el.dom);
38198         }
38199         if(panel.setRegion){
38200             panel.setRegion(this);
38201         }
38202         this.panels.add(panel);
38203         el.setStyle("position", "absolute");
38204         if(!panel.background){
38205             this.setActivePanel(panel);
38206             if(this.config.initialSize && this.panels.getCount()==1){
38207                 this.resizeTo(this.config.initialSize);
38208             }
38209         }
38210         this.fireEvent("paneladded", this, panel);
38211         return panel;
38212     },
38213     
38214     /**
38215      * Returns true if the panel is in this region.
38216      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38217      * @return {Boolean}
38218      */
38219     hasPanel : function(panel){
38220         if(typeof panel == "object"){ // must be panel obj
38221             panel = panel.getId();
38222         }
38223         return this.getPanel(panel) ? true : false;
38224     },
38225     
38226     /**
38227      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38228      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38229      * @param {Boolean} preservePanel Overrides the config preservePanel option
38230      * @return {Roo.ContentPanel} The panel that was removed
38231      */
38232     remove : function(panel, preservePanel){
38233         panel = this.getPanel(panel);
38234         if(!panel){
38235             return null;
38236         }
38237         var e = {};
38238         this.fireEvent("beforeremove", this, panel, e);
38239         if(e.cancel === true){
38240             return null;
38241         }
38242         var panelId = panel.getId();
38243         this.panels.removeKey(panelId);
38244         return panel;
38245     },
38246     
38247     /**
38248      * Returns the panel specified or null if it's not in this region.
38249      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38250      * @return {Roo.ContentPanel}
38251      */
38252     getPanel : function(id){
38253         if(typeof id == "object"){ // must be panel obj
38254             return id;
38255         }
38256         return this.panels.get(id);
38257     },
38258     
38259     /**
38260      * Returns this regions position (north/south/east/west/center).
38261      * @return {String} 
38262      */
38263     getPosition: function(){
38264         return this.position;    
38265     }
38266 });/*
38267  * Based on:
38268  * Ext JS Library 1.1.1
38269  * Copyright(c) 2006-2007, Ext JS, LLC.
38270  *
38271  * Originally Released Under LGPL - original licence link has changed is not relivant.
38272  *
38273  * Fork - LGPL
38274  * <script type="text/javascript">
38275  */
38276  
38277 /**
38278  * @class Roo.bootstrap.layout.Region
38279  * @extends Roo.bootstrap.layout.Basic
38280  * This class represents a region in a layout manager.
38281  
38282  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38283  * @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})
38284  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38285  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38286  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38287  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38288  * @cfg {String}    title           The title for the region (overrides panel titles)
38289  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38290  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38291  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38292  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38293  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38294  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38295  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38296  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38297  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38298  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38299
38300  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38301  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38302  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38303  * @cfg {Number}    width           For East/West panels
38304  * @cfg {Number}    height          For North/South panels
38305  * @cfg {Boolean}   split           To show the splitter
38306  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38307  * 
38308  * @cfg {string}   cls             Extra CSS classes to add to region
38309  * 
38310  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38311  * @cfg {string}   region  the region that it inhabits..
38312  *
38313
38314  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38315  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38316
38317  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38318  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38319  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38320  */
38321 Roo.bootstrap.layout.Region = function(config)
38322 {
38323     this.applyConfig(config);
38324
38325     var mgr = config.mgr;
38326     var pos = config.region;
38327     config.skipConfig = true;
38328     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38329     
38330     if (mgr.el) {
38331         this.onRender(mgr.el);   
38332     }
38333      
38334     this.visible = true;
38335     this.collapsed = false;
38336     this.unrendered_panels = [];
38337 };
38338
38339 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38340
38341     position: '', // set by wrapper (eg. north/south etc..)
38342     unrendered_panels : null,  // unrendered panels.
38343     
38344     tabPosition : false,
38345     
38346     mgr: false, // points to 'Border'
38347     
38348     
38349     createBody : function(){
38350         /** This region's body element 
38351         * @type Roo.Element */
38352         this.bodyEl = this.el.createChild({
38353                 tag: "div",
38354                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38355         });
38356     },
38357
38358     onRender: function(ctr, pos)
38359     {
38360         var dh = Roo.DomHelper;
38361         /** This region's container element 
38362         * @type Roo.Element */
38363         this.el = dh.append(ctr.dom, {
38364                 tag: "div",
38365                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38366             }, true);
38367         /** This region's title element 
38368         * @type Roo.Element */
38369     
38370         this.titleEl = dh.append(this.el.dom,  {
38371                 tag: "div",
38372                 unselectable: "on",
38373                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38374                 children:[
38375                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38376                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38377                 ]
38378             }, true);
38379         
38380         this.titleEl.enableDisplayMode();
38381         /** This region's title text element 
38382         * @type HTMLElement */
38383         this.titleTextEl = this.titleEl.dom.firstChild;
38384         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38385         /*
38386         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38387         this.closeBtn.enableDisplayMode();
38388         this.closeBtn.on("click", this.closeClicked, this);
38389         this.closeBtn.hide();
38390     */
38391         this.createBody(this.config);
38392         if(this.config.hideWhenEmpty){
38393             this.hide();
38394             this.on("paneladded", this.validateVisibility, this);
38395             this.on("panelremoved", this.validateVisibility, this);
38396         }
38397         if(this.autoScroll){
38398             this.bodyEl.setStyle("overflow", "auto");
38399         }else{
38400             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38401         }
38402         //if(c.titlebar !== false){
38403             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38404                 this.titleEl.hide();
38405             }else{
38406                 this.titleEl.show();
38407                 if(this.config.title){
38408                     this.titleTextEl.innerHTML = this.config.title;
38409                 }
38410             }
38411         //}
38412         if(this.config.collapsed){
38413             this.collapse(true);
38414         }
38415         if(this.config.hidden){
38416             this.hide();
38417         }
38418         
38419         if (this.unrendered_panels && this.unrendered_panels.length) {
38420             for (var i =0;i< this.unrendered_panels.length; i++) {
38421                 this.add(this.unrendered_panels[i]);
38422             }
38423             this.unrendered_panels = null;
38424             
38425         }
38426         
38427     },
38428     
38429     applyConfig : function(c)
38430     {
38431         /*
38432          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38433             var dh = Roo.DomHelper;
38434             if(c.titlebar !== false){
38435                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38436                 this.collapseBtn.on("click", this.collapse, this);
38437                 this.collapseBtn.enableDisplayMode();
38438                 /*
38439                 if(c.showPin === true || this.showPin){
38440                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38441                     this.stickBtn.enableDisplayMode();
38442                     this.stickBtn.on("click", this.expand, this);
38443                     this.stickBtn.hide();
38444                 }
38445                 
38446             }
38447             */
38448             /** This region's collapsed element
38449             * @type Roo.Element */
38450             /*
38451              *
38452             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38453                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38454             ]}, true);
38455             
38456             if(c.floatable !== false){
38457                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38458                this.collapsedEl.on("click", this.collapseClick, this);
38459             }
38460
38461             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38462                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38463                    id: "message", unselectable: "on", style:{"float":"left"}});
38464                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38465              }
38466             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38467             this.expandBtn.on("click", this.expand, this);
38468             
38469         }
38470         
38471         if(this.collapseBtn){
38472             this.collapseBtn.setVisible(c.collapsible == true);
38473         }
38474         
38475         this.cmargins = c.cmargins || this.cmargins ||
38476                          (this.position == "west" || this.position == "east" ?
38477                              {top: 0, left: 2, right:2, bottom: 0} :
38478                              {top: 2, left: 0, right:0, bottom: 2});
38479         */
38480         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38481         
38482         
38483         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38484         
38485         this.autoScroll = c.autoScroll || false;
38486         
38487         
38488        
38489         
38490         this.duration = c.duration || .30;
38491         this.slideDuration = c.slideDuration || .45;
38492         this.config = c;
38493        
38494     },
38495     /**
38496      * Returns true if this region is currently visible.
38497      * @return {Boolean}
38498      */
38499     isVisible : function(){
38500         return this.visible;
38501     },
38502
38503     /**
38504      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38505      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38506      */
38507     //setCollapsedTitle : function(title){
38508     //    title = title || "&#160;";
38509      //   if(this.collapsedTitleTextEl){
38510       //      this.collapsedTitleTextEl.innerHTML = title;
38511        // }
38512     //},
38513
38514     getBox : function(){
38515         var b;
38516       //  if(!this.collapsed){
38517             b = this.el.getBox(false, true);
38518        // }else{
38519           //  b = this.collapsedEl.getBox(false, true);
38520         //}
38521         return b;
38522     },
38523
38524     getMargins : function(){
38525         return this.margins;
38526         //return this.collapsed ? this.cmargins : this.margins;
38527     },
38528 /*
38529     highlight : function(){
38530         this.el.addClass("x-layout-panel-dragover");
38531     },
38532
38533     unhighlight : function(){
38534         this.el.removeClass("x-layout-panel-dragover");
38535     },
38536 */
38537     updateBox : function(box)
38538     {
38539         if (!this.bodyEl) {
38540             return; // not rendered yet..
38541         }
38542         
38543         this.box = box;
38544         if(!this.collapsed){
38545             this.el.dom.style.left = box.x + "px";
38546             this.el.dom.style.top = box.y + "px";
38547             this.updateBody(box.width, box.height);
38548         }else{
38549             this.collapsedEl.dom.style.left = box.x + "px";
38550             this.collapsedEl.dom.style.top = box.y + "px";
38551             this.collapsedEl.setSize(box.width, box.height);
38552         }
38553         if(this.tabs){
38554             this.tabs.autoSizeTabs();
38555         }
38556     },
38557
38558     updateBody : function(w, h)
38559     {
38560         if(w !== null){
38561             this.el.setWidth(w);
38562             w -= this.el.getBorderWidth("rl");
38563             if(this.config.adjustments){
38564                 w += this.config.adjustments[0];
38565             }
38566         }
38567         if(h !== null && h > 0){
38568             this.el.setHeight(h);
38569             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38570             h -= this.el.getBorderWidth("tb");
38571             if(this.config.adjustments){
38572                 h += this.config.adjustments[1];
38573             }
38574             this.bodyEl.setHeight(h);
38575             if(this.tabs){
38576                 h = this.tabs.syncHeight(h);
38577             }
38578         }
38579         if(this.panelSize){
38580             w = w !== null ? w : this.panelSize.width;
38581             h = h !== null ? h : this.panelSize.height;
38582         }
38583         if(this.activePanel){
38584             var el = this.activePanel.getEl();
38585             w = w !== null ? w : el.getWidth();
38586             h = h !== null ? h : el.getHeight();
38587             this.panelSize = {width: w, height: h};
38588             this.activePanel.setSize(w, h);
38589         }
38590         if(Roo.isIE && this.tabs){
38591             this.tabs.el.repaint();
38592         }
38593     },
38594
38595     /**
38596      * Returns the container element for this region.
38597      * @return {Roo.Element}
38598      */
38599     getEl : function(){
38600         return this.el;
38601     },
38602
38603     /**
38604      * Hides this region.
38605      */
38606     hide : function(){
38607         //if(!this.collapsed){
38608             this.el.dom.style.left = "-2000px";
38609             this.el.hide();
38610         //}else{
38611          //   this.collapsedEl.dom.style.left = "-2000px";
38612          //   this.collapsedEl.hide();
38613        // }
38614         this.visible = false;
38615         this.fireEvent("visibilitychange", this, false);
38616     },
38617
38618     /**
38619      * Shows this region if it was previously hidden.
38620      */
38621     show : function(){
38622         //if(!this.collapsed){
38623             this.el.show();
38624         //}else{
38625         //    this.collapsedEl.show();
38626        // }
38627         this.visible = true;
38628         this.fireEvent("visibilitychange", this, true);
38629     },
38630 /*
38631     closeClicked : function(){
38632         if(this.activePanel){
38633             this.remove(this.activePanel);
38634         }
38635     },
38636
38637     collapseClick : function(e){
38638         if(this.isSlid){
38639            e.stopPropagation();
38640            this.slideIn();
38641         }else{
38642            e.stopPropagation();
38643            this.slideOut();
38644         }
38645     },
38646 */
38647     /**
38648      * Collapses this region.
38649      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38650      */
38651     /*
38652     collapse : function(skipAnim, skipCheck = false){
38653         if(this.collapsed) {
38654             return;
38655         }
38656         
38657         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38658             
38659             this.collapsed = true;
38660             if(this.split){
38661                 this.split.el.hide();
38662             }
38663             if(this.config.animate && skipAnim !== true){
38664                 this.fireEvent("invalidated", this);
38665                 this.animateCollapse();
38666             }else{
38667                 this.el.setLocation(-20000,-20000);
38668                 this.el.hide();
38669                 this.collapsedEl.show();
38670                 this.fireEvent("collapsed", this);
38671                 this.fireEvent("invalidated", this);
38672             }
38673         }
38674         
38675     },
38676 */
38677     animateCollapse : function(){
38678         // overridden
38679     },
38680
38681     /**
38682      * Expands this region if it was previously collapsed.
38683      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38684      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38685      */
38686     /*
38687     expand : function(e, skipAnim){
38688         if(e) {
38689             e.stopPropagation();
38690         }
38691         if(!this.collapsed || this.el.hasActiveFx()) {
38692             return;
38693         }
38694         if(this.isSlid){
38695             this.afterSlideIn();
38696             skipAnim = true;
38697         }
38698         this.collapsed = false;
38699         if(this.config.animate && skipAnim !== true){
38700             this.animateExpand();
38701         }else{
38702             this.el.show();
38703             if(this.split){
38704                 this.split.el.show();
38705             }
38706             this.collapsedEl.setLocation(-2000,-2000);
38707             this.collapsedEl.hide();
38708             this.fireEvent("invalidated", this);
38709             this.fireEvent("expanded", this);
38710         }
38711     },
38712 */
38713     animateExpand : function(){
38714         // overridden
38715     },
38716
38717     initTabs : function()
38718     {
38719         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38720         
38721         var ts = new Roo.bootstrap.panel.Tabs({
38722             el: this.bodyEl.dom,
38723             region : this,
38724             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38725             disableTooltips: this.config.disableTabTips,
38726             toolbar : this.config.toolbar
38727         });
38728         
38729         if(this.config.hideTabs){
38730             ts.stripWrap.setDisplayed(false);
38731         }
38732         this.tabs = ts;
38733         ts.resizeTabs = this.config.resizeTabs === true;
38734         ts.minTabWidth = this.config.minTabWidth || 40;
38735         ts.maxTabWidth = this.config.maxTabWidth || 250;
38736         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38737         ts.monitorResize = false;
38738         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38739         ts.bodyEl.addClass('roo-layout-tabs-body');
38740         this.panels.each(this.initPanelAsTab, this);
38741     },
38742
38743     initPanelAsTab : function(panel){
38744         var ti = this.tabs.addTab(
38745             panel.getEl().id,
38746             panel.getTitle(),
38747             null,
38748             this.config.closeOnTab && panel.isClosable(),
38749             panel.tpl
38750         );
38751         if(panel.tabTip !== undefined){
38752             ti.setTooltip(panel.tabTip);
38753         }
38754         ti.on("activate", function(){
38755               this.setActivePanel(panel);
38756         }, this);
38757         
38758         if(this.config.closeOnTab){
38759             ti.on("beforeclose", function(t, e){
38760                 e.cancel = true;
38761                 this.remove(panel);
38762             }, this);
38763         }
38764         
38765         panel.tabItem = ti;
38766         
38767         return ti;
38768     },
38769
38770     updatePanelTitle : function(panel, title)
38771     {
38772         if(this.activePanel == panel){
38773             this.updateTitle(title);
38774         }
38775         if(this.tabs){
38776             var ti = this.tabs.getTab(panel.getEl().id);
38777             ti.setText(title);
38778             if(panel.tabTip !== undefined){
38779                 ti.setTooltip(panel.tabTip);
38780             }
38781         }
38782     },
38783
38784     updateTitle : function(title){
38785         if(this.titleTextEl && !this.config.title){
38786             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38787         }
38788     },
38789
38790     setActivePanel : function(panel)
38791     {
38792         panel = this.getPanel(panel);
38793         if(this.activePanel && this.activePanel != panel){
38794             if(this.activePanel.setActiveState(false) === false){
38795                 return;
38796             }
38797         }
38798         this.activePanel = panel;
38799         panel.setActiveState(true);
38800         if(this.panelSize){
38801             panel.setSize(this.panelSize.width, this.panelSize.height);
38802         }
38803         if(this.closeBtn){
38804             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38805         }
38806         this.updateTitle(panel.getTitle());
38807         if(this.tabs){
38808             this.fireEvent("invalidated", this);
38809         }
38810         this.fireEvent("panelactivated", this, panel);
38811     },
38812
38813     /**
38814      * Shows the specified panel.
38815      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38816      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38817      */
38818     showPanel : function(panel)
38819     {
38820         panel = this.getPanel(panel);
38821         if(panel){
38822             if(this.tabs){
38823                 var tab = this.tabs.getTab(panel.getEl().id);
38824                 if(tab.isHidden()){
38825                     this.tabs.unhideTab(tab.id);
38826                 }
38827                 tab.activate();
38828             }else{
38829                 this.setActivePanel(panel);
38830             }
38831         }
38832         return panel;
38833     },
38834
38835     /**
38836      * Get the active panel for this region.
38837      * @return {Roo.ContentPanel} The active panel or null
38838      */
38839     getActivePanel : function(){
38840         return this.activePanel;
38841     },
38842
38843     validateVisibility : function(){
38844         if(this.panels.getCount() < 1){
38845             this.updateTitle("&#160;");
38846             this.closeBtn.hide();
38847             this.hide();
38848         }else{
38849             if(!this.isVisible()){
38850                 this.show();
38851             }
38852         }
38853     },
38854
38855     /**
38856      * Adds the passed ContentPanel(s) to this region.
38857      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38858      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38859      */
38860     add : function(panel)
38861     {
38862         if(arguments.length > 1){
38863             for(var i = 0, len = arguments.length; i < len; i++) {
38864                 this.add(arguments[i]);
38865             }
38866             return null;
38867         }
38868         
38869         // if we have not been rendered yet, then we can not really do much of this..
38870         if (!this.bodyEl) {
38871             this.unrendered_panels.push(panel);
38872             return panel;
38873         }
38874         
38875         
38876         
38877         
38878         if(this.hasPanel(panel)){
38879             this.showPanel(panel);
38880             return panel;
38881         }
38882         panel.setRegion(this);
38883         this.panels.add(panel);
38884        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38885             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38886             // and hide them... ???
38887             this.bodyEl.dom.appendChild(panel.getEl().dom);
38888             if(panel.background !== true){
38889                 this.setActivePanel(panel);
38890             }
38891             this.fireEvent("paneladded", this, panel);
38892             return panel;
38893         }
38894         */
38895         if(!this.tabs){
38896             this.initTabs();
38897         }else{
38898             this.initPanelAsTab(panel);
38899         }
38900         
38901         
38902         if(panel.background !== true){
38903             this.tabs.activate(panel.getEl().id);
38904         }
38905         this.fireEvent("paneladded", this, panel);
38906         return panel;
38907     },
38908
38909     /**
38910      * Hides the tab for the specified panel.
38911      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38912      */
38913     hidePanel : function(panel){
38914         if(this.tabs && (panel = this.getPanel(panel))){
38915             this.tabs.hideTab(panel.getEl().id);
38916         }
38917     },
38918
38919     /**
38920      * Unhides the tab for a previously hidden panel.
38921      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38922      */
38923     unhidePanel : function(panel){
38924         if(this.tabs && (panel = this.getPanel(panel))){
38925             this.tabs.unhideTab(panel.getEl().id);
38926         }
38927     },
38928
38929     clearPanels : function(){
38930         while(this.panels.getCount() > 0){
38931              this.remove(this.panels.first());
38932         }
38933     },
38934
38935     /**
38936      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38937      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38938      * @param {Boolean} preservePanel Overrides the config preservePanel option
38939      * @return {Roo.ContentPanel} The panel that was removed
38940      */
38941     remove : function(panel, preservePanel)
38942     {
38943         panel = this.getPanel(panel);
38944         if(!panel){
38945             return null;
38946         }
38947         var e = {};
38948         this.fireEvent("beforeremove", this, panel, e);
38949         if(e.cancel === true){
38950             return null;
38951         }
38952         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38953         var panelId = panel.getId();
38954         this.panels.removeKey(panelId);
38955         if(preservePanel){
38956             document.body.appendChild(panel.getEl().dom);
38957         }
38958         if(this.tabs){
38959             this.tabs.removeTab(panel.getEl().id);
38960         }else if (!preservePanel){
38961             this.bodyEl.dom.removeChild(panel.getEl().dom);
38962         }
38963         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38964             var p = this.panels.first();
38965             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38966             tempEl.appendChild(p.getEl().dom);
38967             this.bodyEl.update("");
38968             this.bodyEl.dom.appendChild(p.getEl().dom);
38969             tempEl = null;
38970             this.updateTitle(p.getTitle());
38971             this.tabs = null;
38972             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38973             this.setActivePanel(p);
38974         }
38975         panel.setRegion(null);
38976         if(this.activePanel == panel){
38977             this.activePanel = null;
38978         }
38979         if(this.config.autoDestroy !== false && preservePanel !== true){
38980             try{panel.destroy();}catch(e){}
38981         }
38982         this.fireEvent("panelremoved", this, panel);
38983         return panel;
38984     },
38985
38986     /**
38987      * Returns the TabPanel component used by this region
38988      * @return {Roo.TabPanel}
38989      */
38990     getTabs : function(){
38991         return this.tabs;
38992     },
38993
38994     createTool : function(parentEl, className){
38995         var btn = Roo.DomHelper.append(parentEl, {
38996             tag: "div",
38997             cls: "x-layout-tools-button",
38998             children: [ {
38999                 tag: "div",
39000                 cls: "roo-layout-tools-button-inner " + className,
39001                 html: "&#160;"
39002             }]
39003         }, true);
39004         btn.addClassOnOver("roo-layout-tools-button-over");
39005         return btn;
39006     }
39007 });/*
39008  * Based on:
39009  * Ext JS Library 1.1.1
39010  * Copyright(c) 2006-2007, Ext JS, LLC.
39011  *
39012  * Originally Released Under LGPL - original licence link has changed is not relivant.
39013  *
39014  * Fork - LGPL
39015  * <script type="text/javascript">
39016  */
39017  
39018
39019
39020 /**
39021  * @class Roo.SplitLayoutRegion
39022  * @extends Roo.LayoutRegion
39023  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39024  */
39025 Roo.bootstrap.layout.Split = function(config){
39026     this.cursor = config.cursor;
39027     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39028 };
39029
39030 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39031 {
39032     splitTip : "Drag to resize.",
39033     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39034     useSplitTips : false,
39035
39036     applyConfig : function(config){
39037         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39038     },
39039     
39040     onRender : function(ctr,pos) {
39041         
39042         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39043         if(!this.config.split){
39044             return;
39045         }
39046         if(!this.split){
39047             
39048             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39049                             tag: "div",
39050                             id: this.el.id + "-split",
39051                             cls: "roo-layout-split roo-layout-split-"+this.position,
39052                             html: "&#160;"
39053             });
39054             /** The SplitBar for this region 
39055             * @type Roo.SplitBar */
39056             // does not exist yet...
39057             Roo.log([this.position, this.orientation]);
39058             
39059             this.split = new Roo.bootstrap.SplitBar({
39060                 dragElement : splitEl,
39061                 resizingElement: this.el,
39062                 orientation : this.orientation
39063             });
39064             
39065             this.split.on("moved", this.onSplitMove, this);
39066             this.split.useShim = this.config.useShim === true;
39067             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39068             if(this.useSplitTips){
39069                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39070             }
39071             //if(config.collapsible){
39072             //    this.split.el.on("dblclick", this.collapse,  this);
39073             //}
39074         }
39075         if(typeof this.config.minSize != "undefined"){
39076             this.split.minSize = this.config.minSize;
39077         }
39078         if(typeof this.config.maxSize != "undefined"){
39079             this.split.maxSize = this.config.maxSize;
39080         }
39081         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39082             this.hideSplitter();
39083         }
39084         
39085     },
39086
39087     getHMaxSize : function(){
39088          var cmax = this.config.maxSize || 10000;
39089          var center = this.mgr.getRegion("center");
39090          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39091     },
39092
39093     getVMaxSize : function(){
39094          var cmax = this.config.maxSize || 10000;
39095          var center = this.mgr.getRegion("center");
39096          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39097     },
39098
39099     onSplitMove : function(split, newSize){
39100         this.fireEvent("resized", this, newSize);
39101     },
39102     
39103     /** 
39104      * Returns the {@link Roo.SplitBar} for this region.
39105      * @return {Roo.SplitBar}
39106      */
39107     getSplitBar : function(){
39108         return this.split;
39109     },
39110     
39111     hide : function(){
39112         this.hideSplitter();
39113         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39114     },
39115
39116     hideSplitter : function(){
39117         if(this.split){
39118             this.split.el.setLocation(-2000,-2000);
39119             this.split.el.hide();
39120         }
39121     },
39122
39123     show : function(){
39124         if(this.split){
39125             this.split.el.show();
39126         }
39127         Roo.bootstrap.layout.Split.superclass.show.call(this);
39128     },
39129     
39130     beforeSlide: function(){
39131         if(Roo.isGecko){// firefox overflow auto bug workaround
39132             this.bodyEl.clip();
39133             if(this.tabs) {
39134                 this.tabs.bodyEl.clip();
39135             }
39136             if(this.activePanel){
39137                 this.activePanel.getEl().clip();
39138                 
39139                 if(this.activePanel.beforeSlide){
39140                     this.activePanel.beforeSlide();
39141                 }
39142             }
39143         }
39144     },
39145     
39146     afterSlide : function(){
39147         if(Roo.isGecko){// firefox overflow auto bug workaround
39148             this.bodyEl.unclip();
39149             if(this.tabs) {
39150                 this.tabs.bodyEl.unclip();
39151             }
39152             if(this.activePanel){
39153                 this.activePanel.getEl().unclip();
39154                 if(this.activePanel.afterSlide){
39155                     this.activePanel.afterSlide();
39156                 }
39157             }
39158         }
39159     },
39160
39161     initAutoHide : function(){
39162         if(this.autoHide !== false){
39163             if(!this.autoHideHd){
39164                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39165                 this.autoHideHd = {
39166                     "mouseout": function(e){
39167                         if(!e.within(this.el, true)){
39168                             st.delay(500);
39169                         }
39170                     },
39171                     "mouseover" : function(e){
39172                         st.cancel();
39173                     },
39174                     scope : this
39175                 };
39176             }
39177             this.el.on(this.autoHideHd);
39178         }
39179     },
39180
39181     clearAutoHide : function(){
39182         if(this.autoHide !== false){
39183             this.el.un("mouseout", this.autoHideHd.mouseout);
39184             this.el.un("mouseover", this.autoHideHd.mouseover);
39185         }
39186     },
39187
39188     clearMonitor : function(){
39189         Roo.get(document).un("click", this.slideInIf, this);
39190     },
39191
39192     // these names are backwards but not changed for compat
39193     slideOut : function(){
39194         if(this.isSlid || this.el.hasActiveFx()){
39195             return;
39196         }
39197         this.isSlid = true;
39198         if(this.collapseBtn){
39199             this.collapseBtn.hide();
39200         }
39201         this.closeBtnState = this.closeBtn.getStyle('display');
39202         this.closeBtn.hide();
39203         if(this.stickBtn){
39204             this.stickBtn.show();
39205         }
39206         this.el.show();
39207         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39208         this.beforeSlide();
39209         this.el.setStyle("z-index", 10001);
39210         this.el.slideIn(this.getSlideAnchor(), {
39211             callback: function(){
39212                 this.afterSlide();
39213                 this.initAutoHide();
39214                 Roo.get(document).on("click", this.slideInIf, this);
39215                 this.fireEvent("slideshow", this);
39216             },
39217             scope: this,
39218             block: true
39219         });
39220     },
39221
39222     afterSlideIn : function(){
39223         this.clearAutoHide();
39224         this.isSlid = false;
39225         this.clearMonitor();
39226         this.el.setStyle("z-index", "");
39227         if(this.collapseBtn){
39228             this.collapseBtn.show();
39229         }
39230         this.closeBtn.setStyle('display', this.closeBtnState);
39231         if(this.stickBtn){
39232             this.stickBtn.hide();
39233         }
39234         this.fireEvent("slidehide", this);
39235     },
39236
39237     slideIn : function(cb){
39238         if(!this.isSlid || this.el.hasActiveFx()){
39239             Roo.callback(cb);
39240             return;
39241         }
39242         this.isSlid = false;
39243         this.beforeSlide();
39244         this.el.slideOut(this.getSlideAnchor(), {
39245             callback: function(){
39246                 this.el.setLeftTop(-10000, -10000);
39247                 this.afterSlide();
39248                 this.afterSlideIn();
39249                 Roo.callback(cb);
39250             },
39251             scope: this,
39252             block: true
39253         });
39254     },
39255     
39256     slideInIf : function(e){
39257         if(!e.within(this.el)){
39258             this.slideIn();
39259         }
39260     },
39261
39262     animateCollapse : function(){
39263         this.beforeSlide();
39264         this.el.setStyle("z-index", 20000);
39265         var anchor = this.getSlideAnchor();
39266         this.el.slideOut(anchor, {
39267             callback : function(){
39268                 this.el.setStyle("z-index", "");
39269                 this.collapsedEl.slideIn(anchor, {duration:.3});
39270                 this.afterSlide();
39271                 this.el.setLocation(-10000,-10000);
39272                 this.el.hide();
39273                 this.fireEvent("collapsed", this);
39274             },
39275             scope: this,
39276             block: true
39277         });
39278     },
39279
39280     animateExpand : function(){
39281         this.beforeSlide();
39282         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39283         this.el.setStyle("z-index", 20000);
39284         this.collapsedEl.hide({
39285             duration:.1
39286         });
39287         this.el.slideIn(this.getSlideAnchor(), {
39288             callback : function(){
39289                 this.el.setStyle("z-index", "");
39290                 this.afterSlide();
39291                 if(this.split){
39292                     this.split.el.show();
39293                 }
39294                 this.fireEvent("invalidated", this);
39295                 this.fireEvent("expanded", this);
39296             },
39297             scope: this,
39298             block: true
39299         });
39300     },
39301
39302     anchors : {
39303         "west" : "left",
39304         "east" : "right",
39305         "north" : "top",
39306         "south" : "bottom"
39307     },
39308
39309     sanchors : {
39310         "west" : "l",
39311         "east" : "r",
39312         "north" : "t",
39313         "south" : "b"
39314     },
39315
39316     canchors : {
39317         "west" : "tl-tr",
39318         "east" : "tr-tl",
39319         "north" : "tl-bl",
39320         "south" : "bl-tl"
39321     },
39322
39323     getAnchor : function(){
39324         return this.anchors[this.position];
39325     },
39326
39327     getCollapseAnchor : function(){
39328         return this.canchors[this.position];
39329     },
39330
39331     getSlideAnchor : function(){
39332         return this.sanchors[this.position];
39333     },
39334
39335     getAlignAdj : function(){
39336         var cm = this.cmargins;
39337         switch(this.position){
39338             case "west":
39339                 return [0, 0];
39340             break;
39341             case "east":
39342                 return [0, 0];
39343             break;
39344             case "north":
39345                 return [0, 0];
39346             break;
39347             case "south":
39348                 return [0, 0];
39349             break;
39350         }
39351     },
39352
39353     getExpandAdj : function(){
39354         var c = this.collapsedEl, cm = this.cmargins;
39355         switch(this.position){
39356             case "west":
39357                 return [-(cm.right+c.getWidth()+cm.left), 0];
39358             break;
39359             case "east":
39360                 return [cm.right+c.getWidth()+cm.left, 0];
39361             break;
39362             case "north":
39363                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39364             break;
39365             case "south":
39366                 return [0, cm.top+cm.bottom+c.getHeight()];
39367             break;
39368         }
39369     }
39370 });/*
39371  * Based on:
39372  * Ext JS Library 1.1.1
39373  * Copyright(c) 2006-2007, Ext JS, LLC.
39374  *
39375  * Originally Released Under LGPL - original licence link has changed is not relivant.
39376  *
39377  * Fork - LGPL
39378  * <script type="text/javascript">
39379  */
39380 /*
39381  * These classes are private internal classes
39382  */
39383 Roo.bootstrap.layout.Center = function(config){
39384     config.region = "center";
39385     Roo.bootstrap.layout.Region.call(this, config);
39386     this.visible = true;
39387     this.minWidth = config.minWidth || 20;
39388     this.minHeight = config.minHeight || 20;
39389 };
39390
39391 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39392     hide : function(){
39393         // center panel can't be hidden
39394     },
39395     
39396     show : function(){
39397         // center panel can't be hidden
39398     },
39399     
39400     getMinWidth: function(){
39401         return this.minWidth;
39402     },
39403     
39404     getMinHeight: function(){
39405         return this.minHeight;
39406     }
39407 });
39408
39409
39410
39411
39412  
39413
39414
39415
39416
39417
39418
39419 Roo.bootstrap.layout.North = function(config)
39420 {
39421     config.region = 'north';
39422     config.cursor = 'n-resize';
39423     
39424     Roo.bootstrap.layout.Split.call(this, config);
39425     
39426     
39427     if(this.split){
39428         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39429         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39430         this.split.el.addClass("roo-layout-split-v");
39431     }
39432     //var size = config.initialSize || config.height;
39433     //if(this.el && typeof size != "undefined"){
39434     //    this.el.setHeight(size);
39435     //}
39436 };
39437 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39438 {
39439     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39440      
39441      
39442     onRender : function(ctr, pos)
39443     {
39444         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39445         var size = this.config.initialSize || this.config.height;
39446         if(this.el && typeof size != "undefined"){
39447             this.el.setHeight(size);
39448         }
39449     
39450     },
39451     
39452     getBox : function(){
39453         if(this.collapsed){
39454             return this.collapsedEl.getBox();
39455         }
39456         var box = this.el.getBox();
39457         if(this.split){
39458             box.height += this.split.el.getHeight();
39459         }
39460         return box;
39461     },
39462     
39463     updateBox : function(box){
39464         if(this.split && !this.collapsed){
39465             box.height -= this.split.el.getHeight();
39466             this.split.el.setLeft(box.x);
39467             this.split.el.setTop(box.y+box.height);
39468             this.split.el.setWidth(box.width);
39469         }
39470         if(this.collapsed){
39471             this.updateBody(box.width, null);
39472         }
39473         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39474     }
39475 });
39476
39477
39478
39479
39480
39481 Roo.bootstrap.layout.South = function(config){
39482     config.region = 'south';
39483     config.cursor = 's-resize';
39484     Roo.bootstrap.layout.Split.call(this, config);
39485     if(this.split){
39486         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39487         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39488         this.split.el.addClass("roo-layout-split-v");
39489     }
39490     
39491 };
39492
39493 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39494     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39495     
39496     onRender : function(ctr, pos)
39497     {
39498         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39499         var size = this.config.initialSize || this.config.height;
39500         if(this.el && typeof size != "undefined"){
39501             this.el.setHeight(size);
39502         }
39503     
39504     },
39505     
39506     getBox : function(){
39507         if(this.collapsed){
39508             return this.collapsedEl.getBox();
39509         }
39510         var box = this.el.getBox();
39511         if(this.split){
39512             var sh = this.split.el.getHeight();
39513             box.height += sh;
39514             box.y -= sh;
39515         }
39516         return box;
39517     },
39518     
39519     updateBox : function(box){
39520         if(this.split && !this.collapsed){
39521             var sh = this.split.el.getHeight();
39522             box.height -= sh;
39523             box.y += sh;
39524             this.split.el.setLeft(box.x);
39525             this.split.el.setTop(box.y-sh);
39526             this.split.el.setWidth(box.width);
39527         }
39528         if(this.collapsed){
39529             this.updateBody(box.width, null);
39530         }
39531         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39532     }
39533 });
39534
39535 Roo.bootstrap.layout.East = function(config){
39536     config.region = "east";
39537     config.cursor = "e-resize";
39538     Roo.bootstrap.layout.Split.call(this, config);
39539     if(this.split){
39540         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39541         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39542         this.split.el.addClass("roo-layout-split-h");
39543     }
39544     
39545 };
39546 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39547     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39548     
39549     onRender : function(ctr, pos)
39550     {
39551         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39552         var size = this.config.initialSize || this.config.width;
39553         if(this.el && typeof size != "undefined"){
39554             this.el.setWidth(size);
39555         }
39556     
39557     },
39558     
39559     getBox : function(){
39560         if(this.collapsed){
39561             return this.collapsedEl.getBox();
39562         }
39563         var box = this.el.getBox();
39564         if(this.split){
39565             var sw = this.split.el.getWidth();
39566             box.width += sw;
39567             box.x -= sw;
39568         }
39569         return box;
39570     },
39571
39572     updateBox : function(box){
39573         if(this.split && !this.collapsed){
39574             var sw = this.split.el.getWidth();
39575             box.width -= sw;
39576             this.split.el.setLeft(box.x);
39577             this.split.el.setTop(box.y);
39578             this.split.el.setHeight(box.height);
39579             box.x += sw;
39580         }
39581         if(this.collapsed){
39582             this.updateBody(null, box.height);
39583         }
39584         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39585     }
39586 });
39587
39588 Roo.bootstrap.layout.West = function(config){
39589     config.region = "west";
39590     config.cursor = "w-resize";
39591     
39592     Roo.bootstrap.layout.Split.call(this, config);
39593     if(this.split){
39594         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39595         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39596         this.split.el.addClass("roo-layout-split-h");
39597     }
39598     
39599 };
39600 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39601     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39602     
39603     onRender: function(ctr, pos)
39604     {
39605         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39606         var size = this.config.initialSize || this.config.width;
39607         if(typeof size != "undefined"){
39608             this.el.setWidth(size);
39609         }
39610     },
39611     
39612     getBox : function(){
39613         if(this.collapsed){
39614             return this.collapsedEl.getBox();
39615         }
39616         var box = this.el.getBox();
39617         if (box.width == 0) {
39618             box.width = this.config.width; // kludge?
39619         }
39620         if(this.split){
39621             box.width += this.split.el.getWidth();
39622         }
39623         return box;
39624     },
39625     
39626     updateBox : function(box){
39627         if(this.split && !this.collapsed){
39628             var sw = this.split.el.getWidth();
39629             box.width -= sw;
39630             this.split.el.setLeft(box.x+box.width);
39631             this.split.el.setTop(box.y);
39632             this.split.el.setHeight(box.height);
39633         }
39634         if(this.collapsed){
39635             this.updateBody(null, box.height);
39636         }
39637         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39638     }
39639 });Roo.namespace("Roo.bootstrap.panel");/*
39640  * Based on:
39641  * Ext JS Library 1.1.1
39642  * Copyright(c) 2006-2007, Ext JS, LLC.
39643  *
39644  * Originally Released Under LGPL - original licence link has changed is not relivant.
39645  *
39646  * Fork - LGPL
39647  * <script type="text/javascript">
39648  */
39649 /**
39650  * @class Roo.ContentPanel
39651  * @extends Roo.util.Observable
39652  * A basic ContentPanel element.
39653  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39654  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39655  * @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
39656  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39657  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39658  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39659  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39660  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39661  * @cfg {String} title          The title for this panel
39662  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39663  * @cfg {String} url            Calls {@link #setUrl} with this value
39664  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39665  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39666  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39667  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39668  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39669  * @cfg {Boolean} badges render the badges
39670  * @cfg {String} cls  extra classes to use  
39671  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39672
39673  * @constructor
39674  * Create a new ContentPanel.
39675  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39676  * @param {String/Object} config A string to set only the title or a config object
39677  * @param {String} content (optional) Set the HTML content for this panel
39678  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39679  */
39680 Roo.bootstrap.panel.Content = function( config){
39681     
39682     this.tpl = config.tpl || false;
39683     
39684     var el = config.el;
39685     var content = config.content;
39686
39687     if(config.autoCreate){ // xtype is available if this is called from factory
39688         el = Roo.id();
39689     }
39690     this.el = Roo.get(el);
39691     if(!this.el && config && config.autoCreate){
39692         if(typeof config.autoCreate == "object"){
39693             if(!config.autoCreate.id){
39694                 config.autoCreate.id = config.id||el;
39695             }
39696             this.el = Roo.DomHelper.append(document.body,
39697                         config.autoCreate, true);
39698         }else{
39699             var elcfg =  {
39700                 tag: "div",
39701                 cls: (config.cls || '') +
39702                     (config.background ? ' bg-' + config.background : '') +
39703                     " roo-layout-inactive-content",
39704                 id: config.id||el
39705             };
39706             if (config.iframe) {
39707                 elcfg.cn = [
39708                     {
39709                         tag : 'iframe',
39710                         style : 'border: 0px',
39711                         src : 'about:blank'
39712                     }
39713                 ];
39714             }
39715               
39716             if (config.html) {
39717                 elcfg.html = config.html;
39718                 
39719             }
39720                         
39721             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39722             if (config.iframe) {
39723                 this.iframeEl = this.el.select('iframe',true).first();
39724             }
39725             
39726         }
39727     } 
39728     this.closable = false;
39729     this.loaded = false;
39730     this.active = false;
39731    
39732       
39733     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39734         
39735         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39736         
39737         this.wrapEl = this.el; //this.el.wrap();
39738         var ti = [];
39739         if (config.toolbar.items) {
39740             ti = config.toolbar.items ;
39741             delete config.toolbar.items ;
39742         }
39743         
39744         var nitems = [];
39745         this.toolbar.render(this.wrapEl, 'before');
39746         for(var i =0;i < ti.length;i++) {
39747           //  Roo.log(['add child', items[i]]);
39748             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39749         }
39750         this.toolbar.items = nitems;
39751         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39752         delete config.toolbar;
39753         
39754     }
39755     /*
39756     // xtype created footer. - not sure if will work as we normally have to render first..
39757     if (this.footer && !this.footer.el && this.footer.xtype) {
39758         if (!this.wrapEl) {
39759             this.wrapEl = this.el.wrap();
39760         }
39761     
39762         this.footer.container = this.wrapEl.createChild();
39763          
39764         this.footer = Roo.factory(this.footer, Roo);
39765         
39766     }
39767     */
39768     
39769      if(typeof config == "string"){
39770         this.title = config;
39771     }else{
39772         Roo.apply(this, config);
39773     }
39774     
39775     if(this.resizeEl){
39776         this.resizeEl = Roo.get(this.resizeEl, true);
39777     }else{
39778         this.resizeEl = this.el;
39779     }
39780     // handle view.xtype
39781     
39782  
39783     
39784     
39785     this.addEvents({
39786         /**
39787          * @event activate
39788          * Fires when this panel is activated. 
39789          * @param {Roo.ContentPanel} this
39790          */
39791         "activate" : true,
39792         /**
39793          * @event deactivate
39794          * Fires when this panel is activated. 
39795          * @param {Roo.ContentPanel} this
39796          */
39797         "deactivate" : true,
39798
39799         /**
39800          * @event resize
39801          * Fires when this panel is resized if fitToFrame is true.
39802          * @param {Roo.ContentPanel} this
39803          * @param {Number} width The width after any component adjustments
39804          * @param {Number} height The height after any component adjustments
39805          */
39806         "resize" : true,
39807         
39808          /**
39809          * @event render
39810          * Fires when this tab is created
39811          * @param {Roo.ContentPanel} this
39812          */
39813         "render" : true
39814         
39815         
39816         
39817     });
39818     
39819
39820     
39821     
39822     if(this.autoScroll && !this.iframe){
39823         this.resizeEl.setStyle("overflow", "auto");
39824     } else {
39825         // fix randome scrolling
39826         //this.el.on('scroll', function() {
39827         //    Roo.log('fix random scolling');
39828         //    this.scrollTo('top',0); 
39829         //});
39830     }
39831     content = content || this.content;
39832     if(content){
39833         this.setContent(content);
39834     }
39835     if(config && config.url){
39836         this.setUrl(this.url, this.params, this.loadOnce);
39837     }
39838     
39839     
39840     
39841     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39842     
39843     if (this.view && typeof(this.view.xtype) != 'undefined') {
39844         this.view.el = this.el.appendChild(document.createElement("div"));
39845         this.view = Roo.factory(this.view); 
39846         this.view.render  &&  this.view.render(false, '');  
39847     }
39848     
39849     
39850     this.fireEvent('render', this);
39851 };
39852
39853 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39854     
39855     cls : '',
39856     background : '',
39857     
39858     tabTip : '',
39859     
39860     iframe : false,
39861     iframeEl : false,
39862     
39863     setRegion : function(region){
39864         this.region = region;
39865         this.setActiveClass(region && !this.background);
39866     },
39867     
39868     
39869     setActiveClass: function(state)
39870     {
39871         if(state){
39872            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39873            this.el.setStyle('position','relative');
39874         }else{
39875            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39876            this.el.setStyle('position', 'absolute');
39877         } 
39878     },
39879     
39880     /**
39881      * Returns the toolbar for this Panel if one was configured. 
39882      * @return {Roo.Toolbar} 
39883      */
39884     getToolbar : function(){
39885         return this.toolbar;
39886     },
39887     
39888     setActiveState : function(active)
39889     {
39890         this.active = active;
39891         this.setActiveClass(active);
39892         if(!active){
39893             if(this.fireEvent("deactivate", this) === false){
39894                 return false;
39895             }
39896             return true;
39897         }
39898         this.fireEvent("activate", this);
39899         return true;
39900     },
39901     /**
39902      * Updates this panel's element (not for iframe)
39903      * @param {String} content The new content
39904      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39905     */
39906     setContent : function(content, loadScripts){
39907         if (this.iframe) {
39908             return;
39909         }
39910         
39911         this.el.update(content, loadScripts);
39912     },
39913
39914     ignoreResize : function(w, h){
39915         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39916             return true;
39917         }else{
39918             this.lastSize = {width: w, height: h};
39919             return false;
39920         }
39921     },
39922     /**
39923      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39924      * @return {Roo.UpdateManager} The UpdateManager
39925      */
39926     getUpdateManager : function(){
39927         if (this.iframe) {
39928             return false;
39929         }
39930         return this.el.getUpdateManager();
39931     },
39932      /**
39933      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39934      * Does not work with IFRAME contents
39935      * @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:
39936 <pre><code>
39937 panel.load({
39938     url: "your-url.php",
39939     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39940     callback: yourFunction,
39941     scope: yourObject, //(optional scope)
39942     discardUrl: false,
39943     nocache: false,
39944     text: "Loading...",
39945     timeout: 30,
39946     scripts: false
39947 });
39948 </code></pre>
39949      
39950      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39951      * 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.
39952      * @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}
39953      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39954      * @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.
39955      * @return {Roo.ContentPanel} this
39956      */
39957     load : function(){
39958         
39959         if (this.iframe) {
39960             return this;
39961         }
39962         
39963         var um = this.el.getUpdateManager();
39964         um.update.apply(um, arguments);
39965         return this;
39966     },
39967
39968
39969     /**
39970      * 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.
39971      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39972      * @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)
39973      * @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)
39974      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39975      */
39976     setUrl : function(url, params, loadOnce){
39977         if (this.iframe) {
39978             this.iframeEl.dom.src = url;
39979             return false;
39980         }
39981         
39982         if(this.refreshDelegate){
39983             this.removeListener("activate", this.refreshDelegate);
39984         }
39985         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39986         this.on("activate", this.refreshDelegate);
39987         return this.el.getUpdateManager();
39988     },
39989     
39990     _handleRefresh : function(url, params, loadOnce){
39991         if(!loadOnce || !this.loaded){
39992             var updater = this.el.getUpdateManager();
39993             updater.update(url, params, this._setLoaded.createDelegate(this));
39994         }
39995     },
39996     
39997     _setLoaded : function(){
39998         this.loaded = true;
39999     }, 
40000     
40001     /**
40002      * Returns this panel's id
40003      * @return {String} 
40004      */
40005     getId : function(){
40006         return this.el.id;
40007     },
40008     
40009     /** 
40010      * Returns this panel's element - used by regiosn to add.
40011      * @return {Roo.Element} 
40012      */
40013     getEl : function(){
40014         return this.wrapEl || this.el;
40015     },
40016     
40017    
40018     
40019     adjustForComponents : function(width, height)
40020     {
40021         //Roo.log('adjustForComponents ');
40022         if(this.resizeEl != this.el){
40023             width -= this.el.getFrameWidth('lr');
40024             height -= this.el.getFrameWidth('tb');
40025         }
40026         if(this.toolbar){
40027             var te = this.toolbar.getEl();
40028             te.setWidth(width);
40029             height -= te.getHeight();
40030         }
40031         if(this.footer){
40032             var te = this.footer.getEl();
40033             te.setWidth(width);
40034             height -= te.getHeight();
40035         }
40036         
40037         
40038         if(this.adjustments){
40039             width += this.adjustments[0];
40040             height += this.adjustments[1];
40041         }
40042         return {"width": width, "height": height};
40043     },
40044     
40045     setSize : function(width, height){
40046         if(this.fitToFrame && !this.ignoreResize(width, height)){
40047             if(this.fitContainer && this.resizeEl != this.el){
40048                 this.el.setSize(width, height);
40049             }
40050             var size = this.adjustForComponents(width, height);
40051             if (this.iframe) {
40052                 this.iframeEl.setSize(width,height);
40053             }
40054             
40055             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40056             this.fireEvent('resize', this, size.width, size.height);
40057             
40058             
40059         }
40060     },
40061     
40062     /**
40063      * Returns this panel's title
40064      * @return {String} 
40065      */
40066     getTitle : function(){
40067         
40068         if (typeof(this.title) != 'object') {
40069             return this.title;
40070         }
40071         
40072         var t = '';
40073         for (var k in this.title) {
40074             if (!this.title.hasOwnProperty(k)) {
40075                 continue;
40076             }
40077             
40078             if (k.indexOf('-') >= 0) {
40079                 var s = k.split('-');
40080                 for (var i = 0; i<s.length; i++) {
40081                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40082                 }
40083             } else {
40084                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40085             }
40086         }
40087         return t;
40088     },
40089     
40090     /**
40091      * Set this panel's title
40092      * @param {String} title
40093      */
40094     setTitle : function(title){
40095         this.title = title;
40096         if(this.region){
40097             this.region.updatePanelTitle(this, title);
40098         }
40099     },
40100     
40101     /**
40102      * Returns true is this panel was configured to be closable
40103      * @return {Boolean} 
40104      */
40105     isClosable : function(){
40106         return this.closable;
40107     },
40108     
40109     beforeSlide : function(){
40110         this.el.clip();
40111         this.resizeEl.clip();
40112     },
40113     
40114     afterSlide : function(){
40115         this.el.unclip();
40116         this.resizeEl.unclip();
40117     },
40118     
40119     /**
40120      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40121      *   Will fail silently if the {@link #setUrl} method has not been called.
40122      *   This does not activate the panel, just updates its content.
40123      */
40124     refresh : function(){
40125         if(this.refreshDelegate){
40126            this.loaded = false;
40127            this.refreshDelegate();
40128         }
40129     },
40130     
40131     /**
40132      * Destroys this panel
40133      */
40134     destroy : function(){
40135         this.el.removeAllListeners();
40136         var tempEl = document.createElement("span");
40137         tempEl.appendChild(this.el.dom);
40138         tempEl.innerHTML = "";
40139         this.el.remove();
40140         this.el = null;
40141     },
40142     
40143     /**
40144      * form - if the content panel contains a form - this is a reference to it.
40145      * @type {Roo.form.Form}
40146      */
40147     form : false,
40148     /**
40149      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40150      *    This contains a reference to it.
40151      * @type {Roo.View}
40152      */
40153     view : false,
40154     
40155       /**
40156      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40157      * <pre><code>
40158
40159 layout.addxtype({
40160        xtype : 'Form',
40161        items: [ .... ]
40162    }
40163 );
40164
40165 </code></pre>
40166      * @param {Object} cfg Xtype definition of item to add.
40167      */
40168     
40169     
40170     getChildContainer: function () {
40171         return this.getEl();
40172     }
40173     
40174     
40175     /*
40176         var  ret = new Roo.factory(cfg);
40177         return ret;
40178         
40179         
40180         // add form..
40181         if (cfg.xtype.match(/^Form$/)) {
40182             
40183             var el;
40184             //if (this.footer) {
40185             //    el = this.footer.container.insertSibling(false, 'before');
40186             //} else {
40187                 el = this.el.createChild();
40188             //}
40189
40190             this.form = new  Roo.form.Form(cfg);
40191             
40192             
40193             if ( this.form.allItems.length) {
40194                 this.form.render(el.dom);
40195             }
40196             return this.form;
40197         }
40198         // should only have one of theses..
40199         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40200             // views.. should not be just added - used named prop 'view''
40201             
40202             cfg.el = this.el.appendChild(document.createElement("div"));
40203             // factory?
40204             
40205             var ret = new Roo.factory(cfg);
40206              
40207              ret.render && ret.render(false, ''); // render blank..
40208             this.view = ret;
40209             return ret;
40210         }
40211         return false;
40212     }
40213     \*/
40214 });
40215  
40216 /**
40217  * @class Roo.bootstrap.panel.Grid
40218  * @extends Roo.bootstrap.panel.Content
40219  * @constructor
40220  * Create a new GridPanel.
40221  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40222  * @param {Object} config A the config object
40223   
40224  */
40225
40226
40227
40228 Roo.bootstrap.panel.Grid = function(config)
40229 {
40230     
40231       
40232     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40233         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40234
40235     config.el = this.wrapper;
40236     //this.el = this.wrapper;
40237     
40238       if (config.container) {
40239         // ctor'ed from a Border/panel.grid
40240         
40241         
40242         this.wrapper.setStyle("overflow", "hidden");
40243         this.wrapper.addClass('roo-grid-container');
40244
40245     }
40246     
40247     
40248     if(config.toolbar){
40249         var tool_el = this.wrapper.createChild();    
40250         this.toolbar = Roo.factory(config.toolbar);
40251         var ti = [];
40252         if (config.toolbar.items) {
40253             ti = config.toolbar.items ;
40254             delete config.toolbar.items ;
40255         }
40256         
40257         var nitems = [];
40258         this.toolbar.render(tool_el);
40259         for(var i =0;i < ti.length;i++) {
40260           //  Roo.log(['add child', items[i]]);
40261             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40262         }
40263         this.toolbar.items = nitems;
40264         
40265         delete config.toolbar;
40266     }
40267     
40268     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40269     config.grid.scrollBody = true;;
40270     config.grid.monitorWindowResize = false; // turn off autosizing
40271     config.grid.autoHeight = false;
40272     config.grid.autoWidth = false;
40273     
40274     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40275     
40276     if (config.background) {
40277         // render grid on panel activation (if panel background)
40278         this.on('activate', function(gp) {
40279             if (!gp.grid.rendered) {
40280                 gp.grid.render(this.wrapper);
40281                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40282             }
40283         });
40284             
40285     } else {
40286         this.grid.render(this.wrapper);
40287         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40288
40289     }
40290     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40291     // ??? needed ??? config.el = this.wrapper;
40292     
40293     
40294     
40295   
40296     // xtype created footer. - not sure if will work as we normally have to render first..
40297     if (this.footer && !this.footer.el && this.footer.xtype) {
40298         
40299         var ctr = this.grid.getView().getFooterPanel(true);
40300         this.footer.dataSource = this.grid.dataSource;
40301         this.footer = Roo.factory(this.footer, Roo);
40302         this.footer.render(ctr);
40303         
40304     }
40305     
40306     
40307     
40308     
40309      
40310 };
40311
40312 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40313     getId : function(){
40314         return this.grid.id;
40315     },
40316     
40317     /**
40318      * Returns the grid for this panel
40319      * @return {Roo.bootstrap.Table} 
40320      */
40321     getGrid : function(){
40322         return this.grid;    
40323     },
40324     
40325     setSize : function(width, height){
40326         if(!this.ignoreResize(width, height)){
40327             var grid = this.grid;
40328             var size = this.adjustForComponents(width, height);
40329             // tfoot is not a footer?
40330           
40331             
40332             var gridel = grid.getGridEl();
40333             gridel.setSize(size.width, size.height);
40334             
40335             var tbd = grid.getGridEl().select('tbody', true).first();
40336             var thd = grid.getGridEl().select('thead',true).first();
40337             var tbf= grid.getGridEl().select('tfoot', true).first();
40338
40339             if (tbf) {
40340                 size.height -= tbf.getHeight();
40341             }
40342             if (thd) {
40343                 size.height -= thd.getHeight();
40344             }
40345             
40346             tbd.setSize(size.width, size.height );
40347             // this is for the account management tab -seems to work there.
40348             var thd = grid.getGridEl().select('thead',true).first();
40349             //if (tbd) {
40350             //    tbd.setSize(size.width, size.height - thd.getHeight());
40351             //}
40352              
40353             grid.autoSize();
40354         }
40355     },
40356      
40357     
40358     
40359     beforeSlide : function(){
40360         this.grid.getView().scroller.clip();
40361     },
40362     
40363     afterSlide : function(){
40364         this.grid.getView().scroller.unclip();
40365     },
40366     
40367     destroy : function(){
40368         this.grid.destroy();
40369         delete this.grid;
40370         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40371     }
40372 });
40373
40374 /**
40375  * @class Roo.bootstrap.panel.Nest
40376  * @extends Roo.bootstrap.panel.Content
40377  * @constructor
40378  * Create a new Panel, that can contain a layout.Border.
40379  * 
40380  * 
40381  * @param {Roo.BorderLayout} layout The layout for this panel
40382  * @param {String/Object} config A string to set only the title or a config object
40383  */
40384 Roo.bootstrap.panel.Nest = function(config)
40385 {
40386     // construct with only one argument..
40387     /* FIXME - implement nicer consturctors
40388     if (layout.layout) {
40389         config = layout;
40390         layout = config.layout;
40391         delete config.layout;
40392     }
40393     if (layout.xtype && !layout.getEl) {
40394         // then layout needs constructing..
40395         layout = Roo.factory(layout, Roo);
40396     }
40397     */
40398     
40399     config.el =  config.layout.getEl();
40400     
40401     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40402     
40403     config.layout.monitorWindowResize = false; // turn off autosizing
40404     this.layout = config.layout;
40405     this.layout.getEl().addClass("roo-layout-nested-layout");
40406     this.layout.parent = this;
40407     
40408     
40409     
40410     
40411 };
40412
40413 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40414
40415     setSize : function(width, height){
40416         if(!this.ignoreResize(width, height)){
40417             var size = this.adjustForComponents(width, height);
40418             var el = this.layout.getEl();
40419             if (size.height < 1) {
40420                 el.setWidth(size.width);   
40421             } else {
40422                 el.setSize(size.width, size.height);
40423             }
40424             var touch = el.dom.offsetWidth;
40425             this.layout.layout();
40426             // ie requires a double layout on the first pass
40427             if(Roo.isIE && !this.initialized){
40428                 this.initialized = true;
40429                 this.layout.layout();
40430             }
40431         }
40432     },
40433     
40434     // activate all subpanels if not currently active..
40435     
40436     setActiveState : function(active){
40437         this.active = active;
40438         this.setActiveClass(active);
40439         
40440         if(!active){
40441             this.fireEvent("deactivate", this);
40442             return;
40443         }
40444         
40445         this.fireEvent("activate", this);
40446         // not sure if this should happen before or after..
40447         if (!this.layout) {
40448             return; // should not happen..
40449         }
40450         var reg = false;
40451         for (var r in this.layout.regions) {
40452             reg = this.layout.getRegion(r);
40453             if (reg.getActivePanel()) {
40454                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40455                 reg.setActivePanel(reg.getActivePanel());
40456                 continue;
40457             }
40458             if (!reg.panels.length) {
40459                 continue;
40460             }
40461             reg.showPanel(reg.getPanel(0));
40462         }
40463         
40464         
40465         
40466         
40467     },
40468     
40469     /**
40470      * Returns the nested BorderLayout for this panel
40471      * @return {Roo.BorderLayout} 
40472      */
40473     getLayout : function(){
40474         return this.layout;
40475     },
40476     
40477      /**
40478      * Adds a xtype elements to the layout of the nested panel
40479      * <pre><code>
40480
40481 panel.addxtype({
40482        xtype : 'ContentPanel',
40483        region: 'west',
40484        items: [ .... ]
40485    }
40486 );
40487
40488 panel.addxtype({
40489         xtype : 'NestedLayoutPanel',
40490         region: 'west',
40491         layout: {
40492            center: { },
40493            west: { }   
40494         },
40495         items : [ ... list of content panels or nested layout panels.. ]
40496    }
40497 );
40498 </code></pre>
40499      * @param {Object} cfg Xtype definition of item to add.
40500      */
40501     addxtype : function(cfg) {
40502         return this.layout.addxtype(cfg);
40503     
40504     }
40505 });/*
40506  * Based on:
40507  * Ext JS Library 1.1.1
40508  * Copyright(c) 2006-2007, Ext JS, LLC.
40509  *
40510  * Originally Released Under LGPL - original licence link has changed is not relivant.
40511  *
40512  * Fork - LGPL
40513  * <script type="text/javascript">
40514  */
40515 /**
40516  * @class Roo.TabPanel
40517  * @extends Roo.util.Observable
40518  * A lightweight tab container.
40519  * <br><br>
40520  * Usage:
40521  * <pre><code>
40522 // basic tabs 1, built from existing content
40523 var tabs = new Roo.TabPanel("tabs1");
40524 tabs.addTab("script", "View Script");
40525 tabs.addTab("markup", "View Markup");
40526 tabs.activate("script");
40527
40528 // more advanced tabs, built from javascript
40529 var jtabs = new Roo.TabPanel("jtabs");
40530 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40531
40532 // set up the UpdateManager
40533 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40534 var updater = tab2.getUpdateManager();
40535 updater.setDefaultUrl("ajax1.htm");
40536 tab2.on('activate', updater.refresh, updater, true);
40537
40538 // Use setUrl for Ajax loading
40539 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40540 tab3.setUrl("ajax2.htm", null, true);
40541
40542 // Disabled tab
40543 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40544 tab4.disable();
40545
40546 jtabs.activate("jtabs-1");
40547  * </code></pre>
40548  * @constructor
40549  * Create a new TabPanel.
40550  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40551  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40552  */
40553 Roo.bootstrap.panel.Tabs = function(config){
40554     /**
40555     * The container element for this TabPanel.
40556     * @type Roo.Element
40557     */
40558     this.el = Roo.get(config.el);
40559     delete config.el;
40560     if(config){
40561         if(typeof config == "boolean"){
40562             this.tabPosition = config ? "bottom" : "top";
40563         }else{
40564             Roo.apply(this, config);
40565         }
40566     }
40567     
40568     if(this.tabPosition == "bottom"){
40569         // if tabs are at the bottom = create the body first.
40570         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40571         this.el.addClass("roo-tabs-bottom");
40572     }
40573     // next create the tabs holders
40574     
40575     if (this.tabPosition == "west"){
40576         
40577         var reg = this.region; // fake it..
40578         while (reg) {
40579             if (!reg.mgr.parent) {
40580                 break;
40581             }
40582             reg = reg.mgr.parent.region;
40583         }
40584         Roo.log("got nest?");
40585         Roo.log(reg);
40586         if (reg.mgr.getRegion('west')) {
40587             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40588             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40589             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40590             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40591             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40592         
40593             
40594         }
40595         
40596         
40597     } else {
40598      
40599         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40600         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40601         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40602         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40603     }
40604     
40605     
40606     if(Roo.isIE){
40607         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40608     }
40609     
40610     // finally - if tabs are at the top, then create the body last..
40611     if(this.tabPosition != "bottom"){
40612         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40613          * @type Roo.Element
40614          */
40615         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40616         this.el.addClass("roo-tabs-top");
40617     }
40618     this.items = [];
40619
40620     this.bodyEl.setStyle("position", "relative");
40621
40622     this.active = null;
40623     this.activateDelegate = this.activate.createDelegate(this);
40624
40625     this.addEvents({
40626         /**
40627          * @event tabchange
40628          * Fires when the active tab changes
40629          * @param {Roo.TabPanel} this
40630          * @param {Roo.TabPanelItem} activePanel The new active tab
40631          */
40632         "tabchange": true,
40633         /**
40634          * @event beforetabchange
40635          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40636          * @param {Roo.TabPanel} this
40637          * @param {Object} e Set cancel to true on this object to cancel the tab change
40638          * @param {Roo.TabPanelItem} tab The tab being changed to
40639          */
40640         "beforetabchange" : true
40641     });
40642
40643     Roo.EventManager.onWindowResize(this.onResize, this);
40644     this.cpad = this.el.getPadding("lr");
40645     this.hiddenCount = 0;
40646
40647
40648     // toolbar on the tabbar support...
40649     if (this.toolbar) {
40650         alert("no toolbar support yet");
40651         this.toolbar  = false;
40652         /*
40653         var tcfg = this.toolbar;
40654         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40655         this.toolbar = new Roo.Toolbar(tcfg);
40656         if (Roo.isSafari) {
40657             var tbl = tcfg.container.child('table', true);
40658             tbl.setAttribute('width', '100%');
40659         }
40660         */
40661         
40662     }
40663    
40664
40665
40666     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40667 };
40668
40669 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40670     /*
40671      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40672      */
40673     tabPosition : "top",
40674     /*
40675      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40676      */
40677     currentTabWidth : 0,
40678     /*
40679      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40680      */
40681     minTabWidth : 40,
40682     /*
40683      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40684      */
40685     maxTabWidth : 250,
40686     /*
40687      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40688      */
40689     preferredTabWidth : 175,
40690     /*
40691      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40692      */
40693     resizeTabs : false,
40694     /*
40695      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40696      */
40697     monitorResize : true,
40698     /*
40699      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40700      */
40701     toolbar : false,  // set by caller..
40702     
40703     region : false, /// set by caller
40704     
40705     disableTooltips : true, // not used yet...
40706
40707     /**
40708      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40709      * @param {String} id The id of the div to use <b>or create</b>
40710      * @param {String} text The text for the tab
40711      * @param {String} content (optional) Content to put in the TabPanelItem body
40712      * @param {Boolean} closable (optional) True to create a close icon on the tab
40713      * @return {Roo.TabPanelItem} The created TabPanelItem
40714      */
40715     addTab : function(id, text, content, closable, tpl)
40716     {
40717         var item = new Roo.bootstrap.panel.TabItem({
40718             panel: this,
40719             id : id,
40720             text : text,
40721             closable : closable,
40722             tpl : tpl
40723         });
40724         this.addTabItem(item);
40725         if(content){
40726             item.setContent(content);
40727         }
40728         return item;
40729     },
40730
40731     /**
40732      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40733      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40734      * @return {Roo.TabPanelItem}
40735      */
40736     getTab : function(id){
40737         return this.items[id];
40738     },
40739
40740     /**
40741      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40742      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40743      */
40744     hideTab : function(id){
40745         var t = this.items[id];
40746         if(!t.isHidden()){
40747            t.setHidden(true);
40748            this.hiddenCount++;
40749            this.autoSizeTabs();
40750         }
40751     },
40752
40753     /**
40754      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40755      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40756      */
40757     unhideTab : function(id){
40758         var t = this.items[id];
40759         if(t.isHidden()){
40760            t.setHidden(false);
40761            this.hiddenCount--;
40762            this.autoSizeTabs();
40763         }
40764     },
40765
40766     /**
40767      * Adds an existing {@link Roo.TabPanelItem}.
40768      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40769      */
40770     addTabItem : function(item)
40771     {
40772         this.items[item.id] = item;
40773         this.items.push(item);
40774         this.autoSizeTabs();
40775       //  if(this.resizeTabs){
40776     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40777   //         this.autoSizeTabs();
40778 //        }else{
40779 //            item.autoSize();
40780        // }
40781     },
40782
40783     /**
40784      * Removes a {@link Roo.TabPanelItem}.
40785      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40786      */
40787     removeTab : function(id){
40788         var items = this.items;
40789         var tab = items[id];
40790         if(!tab) { return; }
40791         var index = items.indexOf(tab);
40792         if(this.active == tab && items.length > 1){
40793             var newTab = this.getNextAvailable(index);
40794             if(newTab) {
40795                 newTab.activate();
40796             }
40797         }
40798         this.stripEl.dom.removeChild(tab.pnode.dom);
40799         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40800             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40801         }
40802         items.splice(index, 1);
40803         delete this.items[tab.id];
40804         tab.fireEvent("close", tab);
40805         tab.purgeListeners();
40806         this.autoSizeTabs();
40807     },
40808
40809     getNextAvailable : function(start){
40810         var items = this.items;
40811         var index = start;
40812         // look for a next tab that will slide over to
40813         // replace the one being removed
40814         while(index < items.length){
40815             var item = items[++index];
40816             if(item && !item.isHidden()){
40817                 return item;
40818             }
40819         }
40820         // if one isn't found select the previous tab (on the left)
40821         index = start;
40822         while(index >= 0){
40823             var item = items[--index];
40824             if(item && !item.isHidden()){
40825                 return item;
40826             }
40827         }
40828         return null;
40829     },
40830
40831     /**
40832      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40833      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40834      */
40835     disableTab : function(id){
40836         var tab = this.items[id];
40837         if(tab && this.active != tab){
40838             tab.disable();
40839         }
40840     },
40841
40842     /**
40843      * Enables a {@link Roo.TabPanelItem} that is disabled.
40844      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40845      */
40846     enableTab : function(id){
40847         var tab = this.items[id];
40848         tab.enable();
40849     },
40850
40851     /**
40852      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40853      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40854      * @return {Roo.TabPanelItem} The TabPanelItem.
40855      */
40856     activate : function(id)
40857     {
40858         //Roo.log('activite:'  + id);
40859         
40860         var tab = this.items[id];
40861         if(!tab){
40862             return null;
40863         }
40864         if(tab == this.active || tab.disabled){
40865             return tab;
40866         }
40867         var e = {};
40868         this.fireEvent("beforetabchange", this, e, tab);
40869         if(e.cancel !== true && !tab.disabled){
40870             if(this.active){
40871                 this.active.hide();
40872             }
40873             this.active = this.items[id];
40874             this.active.show();
40875             this.fireEvent("tabchange", this, this.active);
40876         }
40877         return tab;
40878     },
40879
40880     /**
40881      * Gets the active {@link Roo.TabPanelItem}.
40882      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40883      */
40884     getActiveTab : function(){
40885         return this.active;
40886     },
40887
40888     /**
40889      * Updates the tab body element to fit the height of the container element
40890      * for overflow scrolling
40891      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40892      */
40893     syncHeight : function(targetHeight){
40894         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40895         var bm = this.bodyEl.getMargins();
40896         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40897         this.bodyEl.setHeight(newHeight);
40898         return newHeight;
40899     },
40900
40901     onResize : function(){
40902         if(this.monitorResize){
40903             this.autoSizeTabs();
40904         }
40905     },
40906
40907     /**
40908      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40909      */
40910     beginUpdate : function(){
40911         this.updating = true;
40912     },
40913
40914     /**
40915      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40916      */
40917     endUpdate : function(){
40918         this.updating = false;
40919         this.autoSizeTabs();
40920     },
40921
40922     /**
40923      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40924      */
40925     autoSizeTabs : function()
40926     {
40927         var count = this.items.length;
40928         var vcount = count - this.hiddenCount;
40929         
40930         if (vcount < 2) {
40931             this.stripEl.hide();
40932         } else {
40933             this.stripEl.show();
40934         }
40935         
40936         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40937             return;
40938         }
40939         
40940         
40941         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40942         var availWidth = Math.floor(w / vcount);
40943         var b = this.stripBody;
40944         if(b.getWidth() > w){
40945             var tabs = this.items;
40946             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40947             if(availWidth < this.minTabWidth){
40948                 /*if(!this.sleft){    // incomplete scrolling code
40949                     this.createScrollButtons();
40950                 }
40951                 this.showScroll();
40952                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40953             }
40954         }else{
40955             if(this.currentTabWidth < this.preferredTabWidth){
40956                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40957             }
40958         }
40959     },
40960
40961     /**
40962      * Returns the number of tabs in this TabPanel.
40963      * @return {Number}
40964      */
40965      getCount : function(){
40966          return this.items.length;
40967      },
40968
40969     /**
40970      * Resizes all the tabs to the passed width
40971      * @param {Number} The new width
40972      */
40973     setTabWidth : function(width){
40974         this.currentTabWidth = width;
40975         for(var i = 0, len = this.items.length; i < len; i++) {
40976                 if(!this.items[i].isHidden()) {
40977                 this.items[i].setWidth(width);
40978             }
40979         }
40980     },
40981
40982     /**
40983      * Destroys this TabPanel
40984      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40985      */
40986     destroy : function(removeEl){
40987         Roo.EventManager.removeResizeListener(this.onResize, this);
40988         for(var i = 0, len = this.items.length; i < len; i++){
40989             this.items[i].purgeListeners();
40990         }
40991         if(removeEl === true){
40992             this.el.update("");
40993             this.el.remove();
40994         }
40995     },
40996     
40997     createStrip : function(container)
40998     {
40999         var strip = document.createElement("nav");
41000         strip.className = Roo.bootstrap.version == 4 ?
41001             "navbar-light bg-light" : 
41002             "navbar navbar-default"; //"x-tabs-wrap";
41003         container.appendChild(strip);
41004         return strip;
41005     },
41006     
41007     createStripList : function(strip)
41008     {
41009         // div wrapper for retard IE
41010         // returns the "tr" element.
41011         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41012         //'<div class="x-tabs-strip-wrap">'+
41013           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41014           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41015         return strip.firstChild; //.firstChild.firstChild.firstChild;
41016     },
41017     createBody : function(container)
41018     {
41019         var body = document.createElement("div");
41020         Roo.id(body, "tab-body");
41021         //Roo.fly(body).addClass("x-tabs-body");
41022         Roo.fly(body).addClass("tab-content");
41023         container.appendChild(body);
41024         return body;
41025     },
41026     createItemBody :function(bodyEl, id){
41027         var body = Roo.getDom(id);
41028         if(!body){
41029             body = document.createElement("div");
41030             body.id = id;
41031         }
41032         //Roo.fly(body).addClass("x-tabs-item-body");
41033         Roo.fly(body).addClass("tab-pane");
41034          bodyEl.insertBefore(body, bodyEl.firstChild);
41035         return body;
41036     },
41037     /** @private */
41038     createStripElements :  function(stripEl, text, closable, tpl)
41039     {
41040         var td = document.createElement("li"); // was td..
41041         td.className = 'nav-item';
41042         
41043         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41044         
41045         
41046         stripEl.appendChild(td);
41047         /*if(closable){
41048             td.className = "x-tabs-closable";
41049             if(!this.closeTpl){
41050                 this.closeTpl = new Roo.Template(
41051                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41052                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41053                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41054                 );
41055             }
41056             var el = this.closeTpl.overwrite(td, {"text": text});
41057             var close = el.getElementsByTagName("div")[0];
41058             var inner = el.getElementsByTagName("em")[0];
41059             return {"el": el, "close": close, "inner": inner};
41060         } else {
41061         */
41062         // not sure what this is..
41063 //            if(!this.tabTpl){
41064                 //this.tabTpl = new Roo.Template(
41065                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41066                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41067                 //);
41068 //                this.tabTpl = new Roo.Template(
41069 //                   '<a href="#">' +
41070 //                   '<span unselectable="on"' +
41071 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41072 //                            ' >{text}</span></a>'
41073 //                );
41074 //                
41075 //            }
41076
41077
41078             var template = tpl || this.tabTpl || false;
41079             
41080             if(!template){
41081                 template =  new Roo.Template(
41082                         Roo.bootstrap.version == 4 ? 
41083                             (
41084                                 '<a class="nav-link" href="#" unselectable="on"' +
41085                                      (this.disableTooltips ? '' : ' title="{text}"') +
41086                                      ' >{text}</a>'
41087                             ) : (
41088                                 '<a class="nav-link" href="#">' +
41089                                 '<span unselectable="on"' +
41090                                          (this.disableTooltips ? '' : ' title="{text}"') +
41091                                     ' >{text}</span></a>'
41092                             )
41093                 );
41094             }
41095             
41096             switch (typeof(template)) {
41097                 case 'object' :
41098                     break;
41099                 case 'string' :
41100                     template = new Roo.Template(template);
41101                     break;
41102                 default :
41103                     break;
41104             }
41105             
41106             var el = template.overwrite(td, {"text": text});
41107             
41108             var inner = el.getElementsByTagName("span")[0];
41109             
41110             return {"el": el, "inner": inner};
41111             
41112     }
41113         
41114     
41115 });
41116
41117 /**
41118  * @class Roo.TabPanelItem
41119  * @extends Roo.util.Observable
41120  * Represents an individual item (tab plus body) in a TabPanel.
41121  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41122  * @param {String} id The id of this TabPanelItem
41123  * @param {String} text The text for the tab of this TabPanelItem
41124  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41125  */
41126 Roo.bootstrap.panel.TabItem = function(config){
41127     /**
41128      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41129      * @type Roo.TabPanel
41130      */
41131     this.tabPanel = config.panel;
41132     /**
41133      * The id for this TabPanelItem
41134      * @type String
41135      */
41136     this.id = config.id;
41137     /** @private */
41138     this.disabled = false;
41139     /** @private */
41140     this.text = config.text;
41141     /** @private */
41142     this.loaded = false;
41143     this.closable = config.closable;
41144
41145     /**
41146      * The body element for this TabPanelItem.
41147      * @type Roo.Element
41148      */
41149     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41150     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41151     this.bodyEl.setStyle("display", "block");
41152     this.bodyEl.setStyle("zoom", "1");
41153     //this.hideAction();
41154
41155     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41156     /** @private */
41157     this.el = Roo.get(els.el);
41158     this.inner = Roo.get(els.inner, true);
41159      this.textEl = Roo.bootstrap.version == 4 ?
41160         this.el : Roo.get(this.el.dom.firstChild, true);
41161
41162     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41163     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41164
41165     
41166 //    this.el.on("mousedown", this.onTabMouseDown, this);
41167     this.el.on("click", this.onTabClick, this);
41168     /** @private */
41169     if(config.closable){
41170         var c = Roo.get(els.close, true);
41171         c.dom.title = this.closeText;
41172         c.addClassOnOver("close-over");
41173         c.on("click", this.closeClick, this);
41174      }
41175
41176     this.addEvents({
41177          /**
41178          * @event activate
41179          * Fires when this tab becomes the active tab.
41180          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41181          * @param {Roo.TabPanelItem} this
41182          */
41183         "activate": true,
41184         /**
41185          * @event beforeclose
41186          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41187          * @param {Roo.TabPanelItem} this
41188          * @param {Object} e Set cancel to true on this object to cancel the close.
41189          */
41190         "beforeclose": true,
41191         /**
41192          * @event close
41193          * Fires when this tab is closed.
41194          * @param {Roo.TabPanelItem} this
41195          */
41196          "close": true,
41197         /**
41198          * @event deactivate
41199          * Fires when this tab is no longer the active tab.
41200          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41201          * @param {Roo.TabPanelItem} this
41202          */
41203          "deactivate" : true
41204     });
41205     this.hidden = false;
41206
41207     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41208 };
41209
41210 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41211            {
41212     purgeListeners : function(){
41213        Roo.util.Observable.prototype.purgeListeners.call(this);
41214        this.el.removeAllListeners();
41215     },
41216     /**
41217      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41218      */
41219     show : function(){
41220         this.status_node.addClass("active");
41221         this.showAction();
41222         if(Roo.isOpera){
41223             this.tabPanel.stripWrap.repaint();
41224         }
41225         this.fireEvent("activate", this.tabPanel, this);
41226     },
41227
41228     /**
41229      * Returns true if this tab is the active tab.
41230      * @return {Boolean}
41231      */
41232     isActive : function(){
41233         return this.tabPanel.getActiveTab() == this;
41234     },
41235
41236     /**
41237      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41238      */
41239     hide : function(){
41240         this.status_node.removeClass("active");
41241         this.hideAction();
41242         this.fireEvent("deactivate", this.tabPanel, this);
41243     },
41244
41245     hideAction : function(){
41246         this.bodyEl.hide();
41247         this.bodyEl.setStyle("position", "absolute");
41248         this.bodyEl.setLeft("-20000px");
41249         this.bodyEl.setTop("-20000px");
41250     },
41251
41252     showAction : function(){
41253         this.bodyEl.setStyle("position", "relative");
41254         this.bodyEl.setTop("");
41255         this.bodyEl.setLeft("");
41256         this.bodyEl.show();
41257     },
41258
41259     /**
41260      * Set the tooltip for the tab.
41261      * @param {String} tooltip The tab's tooltip
41262      */
41263     setTooltip : function(text){
41264         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41265             this.textEl.dom.qtip = text;
41266             this.textEl.dom.removeAttribute('title');
41267         }else{
41268             this.textEl.dom.title = text;
41269         }
41270     },
41271
41272     onTabClick : function(e){
41273         e.preventDefault();
41274         this.tabPanel.activate(this.id);
41275     },
41276
41277     onTabMouseDown : function(e){
41278         e.preventDefault();
41279         this.tabPanel.activate(this.id);
41280     },
41281 /*
41282     getWidth : function(){
41283         return this.inner.getWidth();
41284     },
41285
41286     setWidth : function(width){
41287         var iwidth = width - this.linode.getPadding("lr");
41288         this.inner.setWidth(iwidth);
41289         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41290         this.linode.setWidth(width);
41291     },
41292 */
41293     /**
41294      * Show or hide the tab
41295      * @param {Boolean} hidden True to hide or false to show.
41296      */
41297     setHidden : function(hidden){
41298         this.hidden = hidden;
41299         this.linode.setStyle("display", hidden ? "none" : "");
41300     },
41301
41302     /**
41303      * Returns true if this tab is "hidden"
41304      * @return {Boolean}
41305      */
41306     isHidden : function(){
41307         return this.hidden;
41308     },
41309
41310     /**
41311      * Returns the text for this tab
41312      * @return {String}
41313      */
41314     getText : function(){
41315         return this.text;
41316     },
41317     /*
41318     autoSize : function(){
41319         //this.el.beginMeasure();
41320         this.textEl.setWidth(1);
41321         /*
41322          *  #2804 [new] Tabs in Roojs
41323          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41324          */
41325         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41326         //this.el.endMeasure();
41327     //},
41328
41329     /**
41330      * Sets the text for the tab (Note: this also sets the tooltip text)
41331      * @param {String} text The tab's text and tooltip
41332      */
41333     setText : function(text){
41334         this.text = text;
41335         this.textEl.update(text);
41336         this.setTooltip(text);
41337         //if(!this.tabPanel.resizeTabs){
41338         //    this.autoSize();
41339         //}
41340     },
41341     /**
41342      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41343      */
41344     activate : function(){
41345         this.tabPanel.activate(this.id);
41346     },
41347
41348     /**
41349      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41350      */
41351     disable : function(){
41352         if(this.tabPanel.active != this){
41353             this.disabled = true;
41354             this.status_node.addClass("disabled");
41355         }
41356     },
41357
41358     /**
41359      * Enables this TabPanelItem if it was previously disabled.
41360      */
41361     enable : function(){
41362         this.disabled = false;
41363         this.status_node.removeClass("disabled");
41364     },
41365
41366     /**
41367      * Sets the content for this TabPanelItem.
41368      * @param {String} content The content
41369      * @param {Boolean} loadScripts true to look for and load scripts
41370      */
41371     setContent : function(content, loadScripts){
41372         this.bodyEl.update(content, loadScripts);
41373     },
41374
41375     /**
41376      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41377      * @return {Roo.UpdateManager} The UpdateManager
41378      */
41379     getUpdateManager : function(){
41380         return this.bodyEl.getUpdateManager();
41381     },
41382
41383     /**
41384      * Set a URL to be used to load the content for this TabPanelItem.
41385      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41386      * @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)
41387      * @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)
41388      * @return {Roo.UpdateManager} The UpdateManager
41389      */
41390     setUrl : function(url, params, loadOnce){
41391         if(this.refreshDelegate){
41392             this.un('activate', this.refreshDelegate);
41393         }
41394         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41395         this.on("activate", this.refreshDelegate);
41396         return this.bodyEl.getUpdateManager();
41397     },
41398
41399     /** @private */
41400     _handleRefresh : function(url, params, loadOnce){
41401         if(!loadOnce || !this.loaded){
41402             var updater = this.bodyEl.getUpdateManager();
41403             updater.update(url, params, this._setLoaded.createDelegate(this));
41404         }
41405     },
41406
41407     /**
41408      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41409      *   Will fail silently if the setUrl method has not been called.
41410      *   This does not activate the panel, just updates its content.
41411      */
41412     refresh : function(){
41413         if(this.refreshDelegate){
41414            this.loaded = false;
41415            this.refreshDelegate();
41416         }
41417     },
41418
41419     /** @private */
41420     _setLoaded : function(){
41421         this.loaded = true;
41422     },
41423
41424     /** @private */
41425     closeClick : function(e){
41426         var o = {};
41427         e.stopEvent();
41428         this.fireEvent("beforeclose", this, o);
41429         if(o.cancel !== true){
41430             this.tabPanel.removeTab(this.id);
41431         }
41432     },
41433     /**
41434      * The text displayed in the tooltip for the close icon.
41435      * @type String
41436      */
41437     closeText : "Close this tab"
41438 });
41439 /**
41440 *    This script refer to:
41441 *    Title: International Telephone Input
41442 *    Author: Jack O'Connor
41443 *    Code version:  v12.1.12
41444 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41445 **/
41446
41447 Roo.bootstrap.PhoneInputData = function() {
41448     var d = [
41449       [
41450         "Afghanistan (‫افغانستان‬‎)",
41451         "af",
41452         "93"
41453       ],
41454       [
41455         "Albania (Shqipëri)",
41456         "al",
41457         "355"
41458       ],
41459       [
41460         "Algeria (‫الجزائر‬‎)",
41461         "dz",
41462         "213"
41463       ],
41464       [
41465         "American Samoa",
41466         "as",
41467         "1684"
41468       ],
41469       [
41470         "Andorra",
41471         "ad",
41472         "376"
41473       ],
41474       [
41475         "Angola",
41476         "ao",
41477         "244"
41478       ],
41479       [
41480         "Anguilla",
41481         "ai",
41482         "1264"
41483       ],
41484       [
41485         "Antigua and Barbuda",
41486         "ag",
41487         "1268"
41488       ],
41489       [
41490         "Argentina",
41491         "ar",
41492         "54"
41493       ],
41494       [
41495         "Armenia (Հայաստան)",
41496         "am",
41497         "374"
41498       ],
41499       [
41500         "Aruba",
41501         "aw",
41502         "297"
41503       ],
41504       [
41505         "Australia",
41506         "au",
41507         "61",
41508         0
41509       ],
41510       [
41511         "Austria (Österreich)",
41512         "at",
41513         "43"
41514       ],
41515       [
41516         "Azerbaijan (Azərbaycan)",
41517         "az",
41518         "994"
41519       ],
41520       [
41521         "Bahamas",
41522         "bs",
41523         "1242"
41524       ],
41525       [
41526         "Bahrain (‫البحرين‬‎)",
41527         "bh",
41528         "973"
41529       ],
41530       [
41531         "Bangladesh (বাংলাদেশ)",
41532         "bd",
41533         "880"
41534       ],
41535       [
41536         "Barbados",
41537         "bb",
41538         "1246"
41539       ],
41540       [
41541         "Belarus (Беларусь)",
41542         "by",
41543         "375"
41544       ],
41545       [
41546         "Belgium (België)",
41547         "be",
41548         "32"
41549       ],
41550       [
41551         "Belize",
41552         "bz",
41553         "501"
41554       ],
41555       [
41556         "Benin (Bénin)",
41557         "bj",
41558         "229"
41559       ],
41560       [
41561         "Bermuda",
41562         "bm",
41563         "1441"
41564       ],
41565       [
41566         "Bhutan (འབྲུག)",
41567         "bt",
41568         "975"
41569       ],
41570       [
41571         "Bolivia",
41572         "bo",
41573         "591"
41574       ],
41575       [
41576         "Bosnia and Herzegovina (Босна и Херцеговина)",
41577         "ba",
41578         "387"
41579       ],
41580       [
41581         "Botswana",
41582         "bw",
41583         "267"
41584       ],
41585       [
41586         "Brazil (Brasil)",
41587         "br",
41588         "55"
41589       ],
41590       [
41591         "British Indian Ocean Territory",
41592         "io",
41593         "246"
41594       ],
41595       [
41596         "British Virgin Islands",
41597         "vg",
41598         "1284"
41599       ],
41600       [
41601         "Brunei",
41602         "bn",
41603         "673"
41604       ],
41605       [
41606         "Bulgaria (България)",
41607         "bg",
41608         "359"
41609       ],
41610       [
41611         "Burkina Faso",
41612         "bf",
41613         "226"
41614       ],
41615       [
41616         "Burundi (Uburundi)",
41617         "bi",
41618         "257"
41619       ],
41620       [
41621         "Cambodia (កម្ពុជា)",
41622         "kh",
41623         "855"
41624       ],
41625       [
41626         "Cameroon (Cameroun)",
41627         "cm",
41628         "237"
41629       ],
41630       [
41631         "Canada",
41632         "ca",
41633         "1",
41634         1,
41635         ["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"]
41636       ],
41637       [
41638         "Cape Verde (Kabu Verdi)",
41639         "cv",
41640         "238"
41641       ],
41642       [
41643         "Caribbean Netherlands",
41644         "bq",
41645         "599",
41646         1
41647       ],
41648       [
41649         "Cayman Islands",
41650         "ky",
41651         "1345"
41652       ],
41653       [
41654         "Central African Republic (République centrafricaine)",
41655         "cf",
41656         "236"
41657       ],
41658       [
41659         "Chad (Tchad)",
41660         "td",
41661         "235"
41662       ],
41663       [
41664         "Chile",
41665         "cl",
41666         "56"
41667       ],
41668       [
41669         "China (中国)",
41670         "cn",
41671         "86"
41672       ],
41673       [
41674         "Christmas Island",
41675         "cx",
41676         "61",
41677         2
41678       ],
41679       [
41680         "Cocos (Keeling) Islands",
41681         "cc",
41682         "61",
41683         1
41684       ],
41685       [
41686         "Colombia",
41687         "co",
41688         "57"
41689       ],
41690       [
41691         "Comoros (‫جزر القمر‬‎)",
41692         "km",
41693         "269"
41694       ],
41695       [
41696         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41697         "cd",
41698         "243"
41699       ],
41700       [
41701         "Congo (Republic) (Congo-Brazzaville)",
41702         "cg",
41703         "242"
41704       ],
41705       [
41706         "Cook Islands",
41707         "ck",
41708         "682"
41709       ],
41710       [
41711         "Costa Rica",
41712         "cr",
41713         "506"
41714       ],
41715       [
41716         "Côte d’Ivoire",
41717         "ci",
41718         "225"
41719       ],
41720       [
41721         "Croatia (Hrvatska)",
41722         "hr",
41723         "385"
41724       ],
41725       [
41726         "Cuba",
41727         "cu",
41728         "53"
41729       ],
41730       [
41731         "Curaçao",
41732         "cw",
41733         "599",
41734         0
41735       ],
41736       [
41737         "Cyprus (Κύπρος)",
41738         "cy",
41739         "357"
41740       ],
41741       [
41742         "Czech Republic (Česká republika)",
41743         "cz",
41744         "420"
41745       ],
41746       [
41747         "Denmark (Danmark)",
41748         "dk",
41749         "45"
41750       ],
41751       [
41752         "Djibouti",
41753         "dj",
41754         "253"
41755       ],
41756       [
41757         "Dominica",
41758         "dm",
41759         "1767"
41760       ],
41761       [
41762         "Dominican Republic (República Dominicana)",
41763         "do",
41764         "1",
41765         2,
41766         ["809", "829", "849"]
41767       ],
41768       [
41769         "Ecuador",
41770         "ec",
41771         "593"
41772       ],
41773       [
41774         "Egypt (‫مصر‬‎)",
41775         "eg",
41776         "20"
41777       ],
41778       [
41779         "El Salvador",
41780         "sv",
41781         "503"
41782       ],
41783       [
41784         "Equatorial Guinea (Guinea Ecuatorial)",
41785         "gq",
41786         "240"
41787       ],
41788       [
41789         "Eritrea",
41790         "er",
41791         "291"
41792       ],
41793       [
41794         "Estonia (Eesti)",
41795         "ee",
41796         "372"
41797       ],
41798       [
41799         "Ethiopia",
41800         "et",
41801         "251"
41802       ],
41803       [
41804         "Falkland Islands (Islas Malvinas)",
41805         "fk",
41806         "500"
41807       ],
41808       [
41809         "Faroe Islands (Føroyar)",
41810         "fo",
41811         "298"
41812       ],
41813       [
41814         "Fiji",
41815         "fj",
41816         "679"
41817       ],
41818       [
41819         "Finland (Suomi)",
41820         "fi",
41821         "358",
41822         0
41823       ],
41824       [
41825         "France",
41826         "fr",
41827         "33"
41828       ],
41829       [
41830         "French Guiana (Guyane française)",
41831         "gf",
41832         "594"
41833       ],
41834       [
41835         "French Polynesia (Polynésie française)",
41836         "pf",
41837         "689"
41838       ],
41839       [
41840         "Gabon",
41841         "ga",
41842         "241"
41843       ],
41844       [
41845         "Gambia",
41846         "gm",
41847         "220"
41848       ],
41849       [
41850         "Georgia (საქართველო)",
41851         "ge",
41852         "995"
41853       ],
41854       [
41855         "Germany (Deutschland)",
41856         "de",
41857         "49"
41858       ],
41859       [
41860         "Ghana (Gaana)",
41861         "gh",
41862         "233"
41863       ],
41864       [
41865         "Gibraltar",
41866         "gi",
41867         "350"
41868       ],
41869       [
41870         "Greece (Ελλάδα)",
41871         "gr",
41872         "30"
41873       ],
41874       [
41875         "Greenland (Kalaallit Nunaat)",
41876         "gl",
41877         "299"
41878       ],
41879       [
41880         "Grenada",
41881         "gd",
41882         "1473"
41883       ],
41884       [
41885         "Guadeloupe",
41886         "gp",
41887         "590",
41888         0
41889       ],
41890       [
41891         "Guam",
41892         "gu",
41893         "1671"
41894       ],
41895       [
41896         "Guatemala",
41897         "gt",
41898         "502"
41899       ],
41900       [
41901         "Guernsey",
41902         "gg",
41903         "44",
41904         1
41905       ],
41906       [
41907         "Guinea (Guinée)",
41908         "gn",
41909         "224"
41910       ],
41911       [
41912         "Guinea-Bissau (Guiné Bissau)",
41913         "gw",
41914         "245"
41915       ],
41916       [
41917         "Guyana",
41918         "gy",
41919         "592"
41920       ],
41921       [
41922         "Haiti",
41923         "ht",
41924         "509"
41925       ],
41926       [
41927         "Honduras",
41928         "hn",
41929         "504"
41930       ],
41931       [
41932         "Hong Kong (香港)",
41933         "hk",
41934         "852"
41935       ],
41936       [
41937         "Hungary (Magyarország)",
41938         "hu",
41939         "36"
41940       ],
41941       [
41942         "Iceland (Ísland)",
41943         "is",
41944         "354"
41945       ],
41946       [
41947         "India (भारत)",
41948         "in",
41949         "91"
41950       ],
41951       [
41952         "Indonesia",
41953         "id",
41954         "62"
41955       ],
41956       [
41957         "Iran (‫ایران‬‎)",
41958         "ir",
41959         "98"
41960       ],
41961       [
41962         "Iraq (‫العراق‬‎)",
41963         "iq",
41964         "964"
41965       ],
41966       [
41967         "Ireland",
41968         "ie",
41969         "353"
41970       ],
41971       [
41972         "Isle of Man",
41973         "im",
41974         "44",
41975         2
41976       ],
41977       [
41978         "Israel (‫ישראל‬‎)",
41979         "il",
41980         "972"
41981       ],
41982       [
41983         "Italy (Italia)",
41984         "it",
41985         "39",
41986         0
41987       ],
41988       [
41989         "Jamaica",
41990         "jm",
41991         "1876"
41992       ],
41993       [
41994         "Japan (日本)",
41995         "jp",
41996         "81"
41997       ],
41998       [
41999         "Jersey",
42000         "je",
42001         "44",
42002         3
42003       ],
42004       [
42005         "Jordan (‫الأردن‬‎)",
42006         "jo",
42007         "962"
42008       ],
42009       [
42010         "Kazakhstan (Казахстан)",
42011         "kz",
42012         "7",
42013         1
42014       ],
42015       [
42016         "Kenya",
42017         "ke",
42018         "254"
42019       ],
42020       [
42021         "Kiribati",
42022         "ki",
42023         "686"
42024       ],
42025       [
42026         "Kosovo",
42027         "xk",
42028         "383"
42029       ],
42030       [
42031         "Kuwait (‫الكويت‬‎)",
42032         "kw",
42033         "965"
42034       ],
42035       [
42036         "Kyrgyzstan (Кыргызстан)",
42037         "kg",
42038         "996"
42039       ],
42040       [
42041         "Laos (ລາວ)",
42042         "la",
42043         "856"
42044       ],
42045       [
42046         "Latvia (Latvija)",
42047         "lv",
42048         "371"
42049       ],
42050       [
42051         "Lebanon (‫لبنان‬‎)",
42052         "lb",
42053         "961"
42054       ],
42055       [
42056         "Lesotho",
42057         "ls",
42058         "266"
42059       ],
42060       [
42061         "Liberia",
42062         "lr",
42063         "231"
42064       ],
42065       [
42066         "Libya (‫ليبيا‬‎)",
42067         "ly",
42068         "218"
42069       ],
42070       [
42071         "Liechtenstein",
42072         "li",
42073         "423"
42074       ],
42075       [
42076         "Lithuania (Lietuva)",
42077         "lt",
42078         "370"
42079       ],
42080       [
42081         "Luxembourg",
42082         "lu",
42083         "352"
42084       ],
42085       [
42086         "Macau (澳門)",
42087         "mo",
42088         "853"
42089       ],
42090       [
42091         "Macedonia (FYROM) (Македонија)",
42092         "mk",
42093         "389"
42094       ],
42095       [
42096         "Madagascar (Madagasikara)",
42097         "mg",
42098         "261"
42099       ],
42100       [
42101         "Malawi",
42102         "mw",
42103         "265"
42104       ],
42105       [
42106         "Malaysia",
42107         "my",
42108         "60"
42109       ],
42110       [
42111         "Maldives",
42112         "mv",
42113         "960"
42114       ],
42115       [
42116         "Mali",
42117         "ml",
42118         "223"
42119       ],
42120       [
42121         "Malta",
42122         "mt",
42123         "356"
42124       ],
42125       [
42126         "Marshall Islands",
42127         "mh",
42128         "692"
42129       ],
42130       [
42131         "Martinique",
42132         "mq",
42133         "596"
42134       ],
42135       [
42136         "Mauritania (‫موريتانيا‬‎)",
42137         "mr",
42138         "222"
42139       ],
42140       [
42141         "Mauritius (Moris)",
42142         "mu",
42143         "230"
42144       ],
42145       [
42146         "Mayotte",
42147         "yt",
42148         "262",
42149         1
42150       ],
42151       [
42152         "Mexico (México)",
42153         "mx",
42154         "52"
42155       ],
42156       [
42157         "Micronesia",
42158         "fm",
42159         "691"
42160       ],
42161       [
42162         "Moldova (Republica Moldova)",
42163         "md",
42164         "373"
42165       ],
42166       [
42167         "Monaco",
42168         "mc",
42169         "377"
42170       ],
42171       [
42172         "Mongolia (Монгол)",
42173         "mn",
42174         "976"
42175       ],
42176       [
42177         "Montenegro (Crna Gora)",
42178         "me",
42179         "382"
42180       ],
42181       [
42182         "Montserrat",
42183         "ms",
42184         "1664"
42185       ],
42186       [
42187         "Morocco (‫المغرب‬‎)",
42188         "ma",
42189         "212",
42190         0
42191       ],
42192       [
42193         "Mozambique (Moçambique)",
42194         "mz",
42195         "258"
42196       ],
42197       [
42198         "Myanmar (Burma) (မြန်မာ)",
42199         "mm",
42200         "95"
42201       ],
42202       [
42203         "Namibia (Namibië)",
42204         "na",
42205         "264"
42206       ],
42207       [
42208         "Nauru",
42209         "nr",
42210         "674"
42211       ],
42212       [
42213         "Nepal (नेपाल)",
42214         "np",
42215         "977"
42216       ],
42217       [
42218         "Netherlands (Nederland)",
42219         "nl",
42220         "31"
42221       ],
42222       [
42223         "New Caledonia (Nouvelle-Calédonie)",
42224         "nc",
42225         "687"
42226       ],
42227       [
42228         "New Zealand",
42229         "nz",
42230         "64"
42231       ],
42232       [
42233         "Nicaragua",
42234         "ni",
42235         "505"
42236       ],
42237       [
42238         "Niger (Nijar)",
42239         "ne",
42240         "227"
42241       ],
42242       [
42243         "Nigeria",
42244         "ng",
42245         "234"
42246       ],
42247       [
42248         "Niue",
42249         "nu",
42250         "683"
42251       ],
42252       [
42253         "Norfolk Island",
42254         "nf",
42255         "672"
42256       ],
42257       [
42258         "North Korea (조선 민주주의 인민 공화국)",
42259         "kp",
42260         "850"
42261       ],
42262       [
42263         "Northern Mariana Islands",
42264         "mp",
42265         "1670"
42266       ],
42267       [
42268         "Norway (Norge)",
42269         "no",
42270         "47",
42271         0
42272       ],
42273       [
42274         "Oman (‫عُمان‬‎)",
42275         "om",
42276         "968"
42277       ],
42278       [
42279         "Pakistan (‫پاکستان‬‎)",
42280         "pk",
42281         "92"
42282       ],
42283       [
42284         "Palau",
42285         "pw",
42286         "680"
42287       ],
42288       [
42289         "Palestine (‫فلسطين‬‎)",
42290         "ps",
42291         "970"
42292       ],
42293       [
42294         "Panama (Panamá)",
42295         "pa",
42296         "507"
42297       ],
42298       [
42299         "Papua New Guinea",
42300         "pg",
42301         "675"
42302       ],
42303       [
42304         "Paraguay",
42305         "py",
42306         "595"
42307       ],
42308       [
42309         "Peru (Perú)",
42310         "pe",
42311         "51"
42312       ],
42313       [
42314         "Philippines",
42315         "ph",
42316         "63"
42317       ],
42318       [
42319         "Poland (Polska)",
42320         "pl",
42321         "48"
42322       ],
42323       [
42324         "Portugal",
42325         "pt",
42326         "351"
42327       ],
42328       [
42329         "Puerto Rico",
42330         "pr",
42331         "1",
42332         3,
42333         ["787", "939"]
42334       ],
42335       [
42336         "Qatar (‫قطر‬‎)",
42337         "qa",
42338         "974"
42339       ],
42340       [
42341         "Réunion (La Réunion)",
42342         "re",
42343         "262",
42344         0
42345       ],
42346       [
42347         "Romania (România)",
42348         "ro",
42349         "40"
42350       ],
42351       [
42352         "Russia (Россия)",
42353         "ru",
42354         "7",
42355         0
42356       ],
42357       [
42358         "Rwanda",
42359         "rw",
42360         "250"
42361       ],
42362       [
42363         "Saint Barthélemy",
42364         "bl",
42365         "590",
42366         1
42367       ],
42368       [
42369         "Saint Helena",
42370         "sh",
42371         "290"
42372       ],
42373       [
42374         "Saint Kitts and Nevis",
42375         "kn",
42376         "1869"
42377       ],
42378       [
42379         "Saint Lucia",
42380         "lc",
42381         "1758"
42382       ],
42383       [
42384         "Saint Martin (Saint-Martin (partie française))",
42385         "mf",
42386         "590",
42387         2
42388       ],
42389       [
42390         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42391         "pm",
42392         "508"
42393       ],
42394       [
42395         "Saint Vincent and the Grenadines",
42396         "vc",
42397         "1784"
42398       ],
42399       [
42400         "Samoa",
42401         "ws",
42402         "685"
42403       ],
42404       [
42405         "San Marino",
42406         "sm",
42407         "378"
42408       ],
42409       [
42410         "São Tomé and Príncipe (São Tomé e Príncipe)",
42411         "st",
42412         "239"
42413       ],
42414       [
42415         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42416         "sa",
42417         "966"
42418       ],
42419       [
42420         "Senegal (Sénégal)",
42421         "sn",
42422         "221"
42423       ],
42424       [
42425         "Serbia (Србија)",
42426         "rs",
42427         "381"
42428       ],
42429       [
42430         "Seychelles",
42431         "sc",
42432         "248"
42433       ],
42434       [
42435         "Sierra Leone",
42436         "sl",
42437         "232"
42438       ],
42439       [
42440         "Singapore",
42441         "sg",
42442         "65"
42443       ],
42444       [
42445         "Sint Maarten",
42446         "sx",
42447         "1721"
42448       ],
42449       [
42450         "Slovakia (Slovensko)",
42451         "sk",
42452         "421"
42453       ],
42454       [
42455         "Slovenia (Slovenija)",
42456         "si",
42457         "386"
42458       ],
42459       [
42460         "Solomon Islands",
42461         "sb",
42462         "677"
42463       ],
42464       [
42465         "Somalia (Soomaaliya)",
42466         "so",
42467         "252"
42468       ],
42469       [
42470         "South Africa",
42471         "za",
42472         "27"
42473       ],
42474       [
42475         "South Korea (대한민국)",
42476         "kr",
42477         "82"
42478       ],
42479       [
42480         "South Sudan (‫جنوب السودان‬‎)",
42481         "ss",
42482         "211"
42483       ],
42484       [
42485         "Spain (España)",
42486         "es",
42487         "34"
42488       ],
42489       [
42490         "Sri Lanka (ශ්‍රී ලංකාව)",
42491         "lk",
42492         "94"
42493       ],
42494       [
42495         "Sudan (‫السودان‬‎)",
42496         "sd",
42497         "249"
42498       ],
42499       [
42500         "Suriname",
42501         "sr",
42502         "597"
42503       ],
42504       [
42505         "Svalbard and Jan Mayen",
42506         "sj",
42507         "47",
42508         1
42509       ],
42510       [
42511         "Swaziland",
42512         "sz",
42513         "268"
42514       ],
42515       [
42516         "Sweden (Sverige)",
42517         "se",
42518         "46"
42519       ],
42520       [
42521         "Switzerland (Schweiz)",
42522         "ch",
42523         "41"
42524       ],
42525       [
42526         "Syria (‫سوريا‬‎)",
42527         "sy",
42528         "963"
42529       ],
42530       [
42531         "Taiwan (台灣)",
42532         "tw",
42533         "886"
42534       ],
42535       [
42536         "Tajikistan",
42537         "tj",
42538         "992"
42539       ],
42540       [
42541         "Tanzania",
42542         "tz",
42543         "255"
42544       ],
42545       [
42546         "Thailand (ไทย)",
42547         "th",
42548         "66"
42549       ],
42550       [
42551         "Timor-Leste",
42552         "tl",
42553         "670"
42554       ],
42555       [
42556         "Togo",
42557         "tg",
42558         "228"
42559       ],
42560       [
42561         "Tokelau",
42562         "tk",
42563         "690"
42564       ],
42565       [
42566         "Tonga",
42567         "to",
42568         "676"
42569       ],
42570       [
42571         "Trinidad and Tobago",
42572         "tt",
42573         "1868"
42574       ],
42575       [
42576         "Tunisia (‫تونس‬‎)",
42577         "tn",
42578         "216"
42579       ],
42580       [
42581         "Turkey (Türkiye)",
42582         "tr",
42583         "90"
42584       ],
42585       [
42586         "Turkmenistan",
42587         "tm",
42588         "993"
42589       ],
42590       [
42591         "Turks and Caicos Islands",
42592         "tc",
42593         "1649"
42594       ],
42595       [
42596         "Tuvalu",
42597         "tv",
42598         "688"
42599       ],
42600       [
42601         "U.S. Virgin Islands",
42602         "vi",
42603         "1340"
42604       ],
42605       [
42606         "Uganda",
42607         "ug",
42608         "256"
42609       ],
42610       [
42611         "Ukraine (Україна)",
42612         "ua",
42613         "380"
42614       ],
42615       [
42616         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42617         "ae",
42618         "971"
42619       ],
42620       [
42621         "United Kingdom",
42622         "gb",
42623         "44",
42624         0
42625       ],
42626       [
42627         "United States",
42628         "us",
42629         "1",
42630         0
42631       ],
42632       [
42633         "Uruguay",
42634         "uy",
42635         "598"
42636       ],
42637       [
42638         "Uzbekistan (Oʻzbekiston)",
42639         "uz",
42640         "998"
42641       ],
42642       [
42643         "Vanuatu",
42644         "vu",
42645         "678"
42646       ],
42647       [
42648         "Vatican City (Città del Vaticano)",
42649         "va",
42650         "39",
42651         1
42652       ],
42653       [
42654         "Venezuela",
42655         "ve",
42656         "58"
42657       ],
42658       [
42659         "Vietnam (Việt Nam)",
42660         "vn",
42661         "84"
42662       ],
42663       [
42664         "Wallis and Futuna (Wallis-et-Futuna)",
42665         "wf",
42666         "681"
42667       ],
42668       [
42669         "Western Sahara (‫الصحراء الغربية‬‎)",
42670         "eh",
42671         "212",
42672         1
42673       ],
42674       [
42675         "Yemen (‫اليمن‬‎)",
42676         "ye",
42677         "967"
42678       ],
42679       [
42680         "Zambia",
42681         "zm",
42682         "260"
42683       ],
42684       [
42685         "Zimbabwe",
42686         "zw",
42687         "263"
42688       ],
42689       [
42690         "Åland Islands",
42691         "ax",
42692         "358",
42693         1
42694       ]
42695   ];
42696   
42697   return d;
42698 }/**
42699 *    This script refer to:
42700 *    Title: International Telephone Input
42701 *    Author: Jack O'Connor
42702 *    Code version:  v12.1.12
42703 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42704 **/
42705
42706 /**
42707  * @class Roo.bootstrap.PhoneInput
42708  * @extends Roo.bootstrap.TriggerField
42709  * An input with International dial-code selection
42710  
42711  * @cfg {String} defaultDialCode default '+852'
42712  * @cfg {Array} preferedCountries default []
42713   
42714  * @constructor
42715  * Create a new PhoneInput.
42716  * @param {Object} config Configuration options
42717  */
42718
42719 Roo.bootstrap.PhoneInput = function(config) {
42720     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42721 };
42722
42723 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42724         
42725         listWidth: undefined,
42726         
42727         selectedClass: 'active',
42728         
42729         invalidClass : "has-warning",
42730         
42731         validClass: 'has-success',
42732         
42733         allowed: '0123456789',
42734         
42735         max_length: 15,
42736         
42737         /**
42738          * @cfg {String} defaultDialCode The default dial code when initializing the input
42739          */
42740         defaultDialCode: '+852',
42741         
42742         /**
42743          * @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
42744          */
42745         preferedCountries: false,
42746         
42747         getAutoCreate : function()
42748         {
42749             var data = Roo.bootstrap.PhoneInputData();
42750             var align = this.labelAlign || this.parentLabelAlign();
42751             var id = Roo.id();
42752             
42753             this.allCountries = [];
42754             this.dialCodeMapping = [];
42755             
42756             for (var i = 0; i < data.length; i++) {
42757               var c = data[i];
42758               this.allCountries[i] = {
42759                 name: c[0],
42760                 iso2: c[1],
42761                 dialCode: c[2],
42762                 priority: c[3] || 0,
42763                 areaCodes: c[4] || null
42764               };
42765               this.dialCodeMapping[c[2]] = {
42766                   name: c[0],
42767                   iso2: c[1],
42768                   priority: c[3] || 0,
42769                   areaCodes: c[4] || null
42770               };
42771             }
42772             
42773             var cfg = {
42774                 cls: 'form-group',
42775                 cn: []
42776             };
42777             
42778             var input =  {
42779                 tag: 'input',
42780                 id : id,
42781                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42782                 maxlength: this.max_length,
42783                 cls : 'form-control tel-input',
42784                 autocomplete: 'new-password'
42785             };
42786             
42787             var hiddenInput = {
42788                 tag: 'input',
42789                 type: 'hidden',
42790                 cls: 'hidden-tel-input'
42791             };
42792             
42793             if (this.name) {
42794                 hiddenInput.name = this.name;
42795             }
42796             
42797             if (this.disabled) {
42798                 input.disabled = true;
42799             }
42800             
42801             var flag_container = {
42802                 tag: 'div',
42803                 cls: 'flag-box',
42804                 cn: [
42805                     {
42806                         tag: 'div',
42807                         cls: 'flag'
42808                     },
42809                     {
42810                         tag: 'div',
42811                         cls: 'caret'
42812                     }
42813                 ]
42814             };
42815             
42816             var box = {
42817                 tag: 'div',
42818                 cls: this.hasFeedback ? 'has-feedback' : '',
42819                 cn: [
42820                     hiddenInput,
42821                     input,
42822                     {
42823                         tag: 'input',
42824                         cls: 'dial-code-holder',
42825                         disabled: true
42826                     }
42827                 ]
42828             };
42829             
42830             var container = {
42831                 cls: 'roo-select2-container input-group',
42832                 cn: [
42833                     flag_container,
42834                     box
42835                 ]
42836             };
42837             
42838             if (this.fieldLabel.length) {
42839                 var indicator = {
42840                     tag: 'i',
42841                     tooltip: 'This field is required'
42842                 };
42843                 
42844                 var label = {
42845                     tag: 'label',
42846                     'for':  id,
42847                     cls: 'control-label',
42848                     cn: []
42849                 };
42850                 
42851                 var label_text = {
42852                     tag: 'span',
42853                     html: this.fieldLabel
42854                 };
42855                 
42856                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42857                 label.cn = [
42858                     indicator,
42859                     label_text
42860                 ];
42861                 
42862                 if(this.indicatorpos == 'right') {
42863                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42864                     label.cn = [
42865                         label_text,
42866                         indicator
42867                     ];
42868                 }
42869                 
42870                 if(align == 'left') {
42871                     container = {
42872                         tag: 'div',
42873                         cn: [
42874                             container
42875                         ]
42876                     };
42877                     
42878                     if(this.labelWidth > 12){
42879                         label.style = "width: " + this.labelWidth + 'px';
42880                     }
42881                     if(this.labelWidth < 13 && this.labelmd == 0){
42882                         this.labelmd = this.labelWidth;
42883                     }
42884                     if(this.labellg > 0){
42885                         label.cls += ' col-lg-' + this.labellg;
42886                         input.cls += ' col-lg-' + (12 - this.labellg);
42887                     }
42888                     if(this.labelmd > 0){
42889                         label.cls += ' col-md-' + this.labelmd;
42890                         container.cls += ' col-md-' + (12 - this.labelmd);
42891                     }
42892                     if(this.labelsm > 0){
42893                         label.cls += ' col-sm-' + this.labelsm;
42894                         container.cls += ' col-sm-' + (12 - this.labelsm);
42895                     }
42896                     if(this.labelxs > 0){
42897                         label.cls += ' col-xs-' + this.labelxs;
42898                         container.cls += ' col-xs-' + (12 - this.labelxs);
42899                     }
42900                 }
42901             }
42902             
42903             cfg.cn = [
42904                 label,
42905                 container
42906             ];
42907             
42908             var settings = this;
42909             
42910             ['xs','sm','md','lg'].map(function(size){
42911                 if (settings[size]) {
42912                     cfg.cls += ' col-' + size + '-' + settings[size];
42913                 }
42914             });
42915             
42916             this.store = new Roo.data.Store({
42917                 proxy : new Roo.data.MemoryProxy({}),
42918                 reader : new Roo.data.JsonReader({
42919                     fields : [
42920                         {
42921                             'name' : 'name',
42922                             'type' : 'string'
42923                         },
42924                         {
42925                             'name' : 'iso2',
42926                             'type' : 'string'
42927                         },
42928                         {
42929                             'name' : 'dialCode',
42930                             'type' : 'string'
42931                         },
42932                         {
42933                             'name' : 'priority',
42934                             'type' : 'string'
42935                         },
42936                         {
42937                             'name' : 'areaCodes',
42938                             'type' : 'string'
42939                         }
42940                     ]
42941                 })
42942             });
42943             
42944             if(!this.preferedCountries) {
42945                 this.preferedCountries = [
42946                     'hk',
42947                     'gb',
42948                     'us'
42949                 ];
42950             }
42951             
42952             var p = this.preferedCountries.reverse();
42953             
42954             if(p) {
42955                 for (var i = 0; i < p.length; i++) {
42956                     for (var j = 0; j < this.allCountries.length; j++) {
42957                         if(this.allCountries[j].iso2 == p[i]) {
42958                             var t = this.allCountries[j];
42959                             this.allCountries.splice(j,1);
42960                             this.allCountries.unshift(t);
42961                         }
42962                     } 
42963                 }
42964             }
42965             
42966             this.store.proxy.data = {
42967                 success: true,
42968                 data: this.allCountries
42969             };
42970             
42971             return cfg;
42972         },
42973         
42974         initEvents : function()
42975         {
42976             this.createList();
42977             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42978             
42979             this.indicator = this.indicatorEl();
42980             this.flag = this.flagEl();
42981             this.dialCodeHolder = this.dialCodeHolderEl();
42982             
42983             this.trigger = this.el.select('div.flag-box',true).first();
42984             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42985             
42986             var _this = this;
42987             
42988             (function(){
42989                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42990                 _this.list.setWidth(lw);
42991             }).defer(100);
42992             
42993             this.list.on('mouseover', this.onViewOver, this);
42994             this.list.on('mousemove', this.onViewMove, this);
42995             this.inputEl().on("keyup", this.onKeyUp, this);
42996             this.inputEl().on("keypress", this.onKeyPress, this);
42997             
42998             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42999
43000             this.view = new Roo.View(this.list, this.tpl, {
43001                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43002             });
43003             
43004             this.view.on('click', this.onViewClick, this);
43005             this.setValue(this.defaultDialCode);
43006         },
43007         
43008         onTriggerClick : function(e)
43009         {
43010             Roo.log('trigger click');
43011             if(this.disabled){
43012                 return;
43013             }
43014             
43015             if(this.isExpanded()){
43016                 this.collapse();
43017                 this.hasFocus = false;
43018             }else {
43019                 this.store.load({});
43020                 this.hasFocus = true;
43021                 this.expand();
43022             }
43023         },
43024         
43025         isExpanded : function()
43026         {
43027             return this.list.isVisible();
43028         },
43029         
43030         collapse : function()
43031         {
43032             if(!this.isExpanded()){
43033                 return;
43034             }
43035             this.list.hide();
43036             Roo.get(document).un('mousedown', this.collapseIf, this);
43037             Roo.get(document).un('mousewheel', this.collapseIf, this);
43038             this.fireEvent('collapse', this);
43039             this.validate();
43040         },
43041         
43042         expand : function()
43043         {
43044             Roo.log('expand');
43045
43046             if(this.isExpanded() || !this.hasFocus){
43047                 return;
43048             }
43049             
43050             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43051             this.list.setWidth(lw);
43052             
43053             this.list.show();
43054             this.restrictHeight();
43055             
43056             Roo.get(document).on('mousedown', this.collapseIf, this);
43057             Roo.get(document).on('mousewheel', this.collapseIf, this);
43058             
43059             this.fireEvent('expand', this);
43060         },
43061         
43062         restrictHeight : function()
43063         {
43064             this.list.alignTo(this.inputEl(), this.listAlign);
43065             this.list.alignTo(this.inputEl(), this.listAlign);
43066         },
43067         
43068         onViewOver : function(e, t)
43069         {
43070             if(this.inKeyMode){
43071                 return;
43072             }
43073             var item = this.view.findItemFromChild(t);
43074             
43075             if(item){
43076                 var index = this.view.indexOf(item);
43077                 this.select(index, false);
43078             }
43079         },
43080
43081         // private
43082         onViewClick : function(view, doFocus, el, e)
43083         {
43084             var index = this.view.getSelectedIndexes()[0];
43085             
43086             var r = this.store.getAt(index);
43087             
43088             if(r){
43089                 this.onSelect(r, index);
43090             }
43091             if(doFocus !== false && !this.blockFocus){
43092                 this.inputEl().focus();
43093             }
43094         },
43095         
43096         onViewMove : function(e, t)
43097         {
43098             this.inKeyMode = false;
43099         },
43100         
43101         select : function(index, scrollIntoView)
43102         {
43103             this.selectedIndex = index;
43104             this.view.select(index);
43105             if(scrollIntoView !== false){
43106                 var el = this.view.getNode(index);
43107                 if(el){
43108                     this.list.scrollChildIntoView(el, false);
43109                 }
43110             }
43111         },
43112         
43113         createList : function()
43114         {
43115             this.list = Roo.get(document.body).createChild({
43116                 tag: 'ul',
43117                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43118                 style: 'display:none'
43119             });
43120             
43121             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43122         },
43123         
43124         collapseIf : function(e)
43125         {
43126             var in_combo  = e.within(this.el);
43127             var in_list =  e.within(this.list);
43128             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43129             
43130             if (in_combo || in_list || is_list) {
43131                 return;
43132             }
43133             this.collapse();
43134         },
43135         
43136         onSelect : function(record, index)
43137         {
43138             if(this.fireEvent('beforeselect', this, record, index) !== false){
43139                 
43140                 this.setFlagClass(record.data.iso2);
43141                 this.setDialCode(record.data.dialCode);
43142                 this.hasFocus = false;
43143                 this.collapse();
43144                 this.fireEvent('select', this, record, index);
43145             }
43146         },
43147         
43148         flagEl : function()
43149         {
43150             var flag = this.el.select('div.flag',true).first();
43151             if(!flag){
43152                 return false;
43153             }
43154             return flag;
43155         },
43156         
43157         dialCodeHolderEl : function()
43158         {
43159             var d = this.el.select('input.dial-code-holder',true).first();
43160             if(!d){
43161                 return false;
43162             }
43163             return d;
43164         },
43165         
43166         setDialCode : function(v)
43167         {
43168             this.dialCodeHolder.dom.value = '+'+v;
43169         },
43170         
43171         setFlagClass : function(n)
43172         {
43173             this.flag.dom.className = 'flag '+n;
43174         },
43175         
43176         getValue : function()
43177         {
43178             var v = this.inputEl().getValue();
43179             if(this.dialCodeHolder) {
43180                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43181             }
43182             return v;
43183         },
43184         
43185         setValue : function(v)
43186         {
43187             var d = this.getDialCode(v);
43188             
43189             //invalid dial code
43190             if(v.length == 0 || !d || d.length == 0) {
43191                 if(this.rendered){
43192                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43193                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43194                 }
43195                 return;
43196             }
43197             
43198             //valid dial code
43199             this.setFlagClass(this.dialCodeMapping[d].iso2);
43200             this.setDialCode(d);
43201             this.inputEl().dom.value = v.replace('+'+d,'');
43202             this.hiddenEl().dom.value = this.getValue();
43203             
43204             this.validate();
43205         },
43206         
43207         getDialCode : function(v)
43208         {
43209             v = v ||  '';
43210             
43211             if (v.length == 0) {
43212                 return this.dialCodeHolder.dom.value;
43213             }
43214             
43215             var dialCode = "";
43216             if (v.charAt(0) != "+") {
43217                 return false;
43218             }
43219             var numericChars = "";
43220             for (var i = 1; i < v.length; i++) {
43221               var c = v.charAt(i);
43222               if (!isNaN(c)) {
43223                 numericChars += c;
43224                 if (this.dialCodeMapping[numericChars]) {
43225                   dialCode = v.substr(1, i);
43226                 }
43227                 if (numericChars.length == 4) {
43228                   break;
43229                 }
43230               }
43231             }
43232             return dialCode;
43233         },
43234         
43235         reset : function()
43236         {
43237             this.setValue(this.defaultDialCode);
43238             this.validate();
43239         },
43240         
43241         hiddenEl : function()
43242         {
43243             return this.el.select('input.hidden-tel-input',true).first();
43244         },
43245         
43246         // after setting val
43247         onKeyUp : function(e){
43248             this.setValue(this.getValue());
43249         },
43250         
43251         onKeyPress : function(e){
43252             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43253                 e.stopEvent();
43254             }
43255         }
43256         
43257 });
43258 /**
43259  * @class Roo.bootstrap.MoneyField
43260  * @extends Roo.bootstrap.ComboBox
43261  * Bootstrap MoneyField class
43262  * 
43263  * @constructor
43264  * Create a new MoneyField.
43265  * @param {Object} config Configuration options
43266  */
43267
43268 Roo.bootstrap.MoneyField = function(config) {
43269     
43270     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43271     
43272 };
43273
43274 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43275     
43276     /**
43277      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43278      */
43279     allowDecimals : true,
43280     /**
43281      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43282      */
43283     decimalSeparator : ".",
43284     /**
43285      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43286      */
43287     decimalPrecision : 0,
43288     /**
43289      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43290      */
43291     allowNegative : true,
43292     /**
43293      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43294      */
43295     allowZero: true,
43296     /**
43297      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43298      */
43299     minValue : Number.NEGATIVE_INFINITY,
43300     /**
43301      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43302      */
43303     maxValue : Number.MAX_VALUE,
43304     /**
43305      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43306      */
43307     minText : "The minimum value for this field is {0}",
43308     /**
43309      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43310      */
43311     maxText : "The maximum value for this field is {0}",
43312     /**
43313      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43314      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43315      */
43316     nanText : "{0} is not a valid number",
43317     /**
43318      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43319      */
43320     castInt : true,
43321     /**
43322      * @cfg {String} defaults currency of the MoneyField
43323      * value should be in lkey
43324      */
43325     defaultCurrency : false,
43326     /**
43327      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43328      */
43329     thousandsDelimiter : false,
43330     /**
43331      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43332      */
43333     max_length: false,
43334     
43335     inputlg : 9,
43336     inputmd : 9,
43337     inputsm : 9,
43338     inputxs : 6,
43339     
43340     store : false,
43341     
43342     getAutoCreate : function()
43343     {
43344         var align = this.labelAlign || this.parentLabelAlign();
43345         
43346         var id = Roo.id();
43347
43348         var cfg = {
43349             cls: 'form-group',
43350             cn: []
43351         };
43352
43353         var input =  {
43354             tag: 'input',
43355             id : id,
43356             cls : 'form-control roo-money-amount-input',
43357             autocomplete: 'new-password'
43358         };
43359         
43360         var hiddenInput = {
43361             tag: 'input',
43362             type: 'hidden',
43363             id: Roo.id(),
43364             cls: 'hidden-number-input'
43365         };
43366         
43367         if(this.max_length) {
43368             input.maxlength = this.max_length; 
43369         }
43370         
43371         if (this.name) {
43372             hiddenInput.name = this.name;
43373         }
43374
43375         if (this.disabled) {
43376             input.disabled = true;
43377         }
43378
43379         var clg = 12 - this.inputlg;
43380         var cmd = 12 - this.inputmd;
43381         var csm = 12 - this.inputsm;
43382         var cxs = 12 - this.inputxs;
43383         
43384         var container = {
43385             tag : 'div',
43386             cls : 'row roo-money-field',
43387             cn : [
43388                 {
43389                     tag : 'div',
43390                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43391                     cn : [
43392                         {
43393                             tag : 'div',
43394                             cls: 'roo-select2-container input-group',
43395                             cn: [
43396                                 {
43397                                     tag : 'input',
43398                                     cls : 'form-control roo-money-currency-input',
43399                                     autocomplete: 'new-password',
43400                                     readOnly : 1,
43401                                     name : this.currencyName
43402                                 },
43403                                 {
43404                                     tag :'span',
43405                                     cls : 'input-group-addon',
43406                                     cn : [
43407                                         {
43408                                             tag: 'span',
43409                                             cls: 'caret'
43410                                         }
43411                                     ]
43412                                 }
43413                             ]
43414                         }
43415                     ]
43416                 },
43417                 {
43418                     tag : 'div',
43419                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43420                     cn : [
43421                         {
43422                             tag: 'div',
43423                             cls: this.hasFeedback ? 'has-feedback' : '',
43424                             cn: [
43425                                 input
43426                             ]
43427                         }
43428                     ]
43429                 }
43430             ]
43431             
43432         };
43433         
43434         if (this.fieldLabel.length) {
43435             var indicator = {
43436                 tag: 'i',
43437                 tooltip: 'This field is required'
43438             };
43439
43440             var label = {
43441                 tag: 'label',
43442                 'for':  id,
43443                 cls: 'control-label',
43444                 cn: []
43445             };
43446
43447             var label_text = {
43448                 tag: 'span',
43449                 html: this.fieldLabel
43450             };
43451
43452             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43453             label.cn = [
43454                 indicator,
43455                 label_text
43456             ];
43457
43458             if(this.indicatorpos == 'right') {
43459                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43460                 label.cn = [
43461                     label_text,
43462                     indicator
43463                 ];
43464             }
43465
43466             if(align == 'left') {
43467                 container = {
43468                     tag: 'div',
43469                     cn: [
43470                         container
43471                     ]
43472                 };
43473
43474                 if(this.labelWidth > 12){
43475                     label.style = "width: " + this.labelWidth + 'px';
43476                 }
43477                 if(this.labelWidth < 13 && this.labelmd == 0){
43478                     this.labelmd = this.labelWidth;
43479                 }
43480                 if(this.labellg > 0){
43481                     label.cls += ' col-lg-' + this.labellg;
43482                     input.cls += ' col-lg-' + (12 - this.labellg);
43483                 }
43484                 if(this.labelmd > 0){
43485                     label.cls += ' col-md-' + this.labelmd;
43486                     container.cls += ' col-md-' + (12 - this.labelmd);
43487                 }
43488                 if(this.labelsm > 0){
43489                     label.cls += ' col-sm-' + this.labelsm;
43490                     container.cls += ' col-sm-' + (12 - this.labelsm);
43491                 }
43492                 if(this.labelxs > 0){
43493                     label.cls += ' col-xs-' + this.labelxs;
43494                     container.cls += ' col-xs-' + (12 - this.labelxs);
43495                 }
43496             }
43497         }
43498
43499         cfg.cn = [
43500             label,
43501             container,
43502             hiddenInput
43503         ];
43504         
43505         var settings = this;
43506
43507         ['xs','sm','md','lg'].map(function(size){
43508             if (settings[size]) {
43509                 cfg.cls += ' col-' + size + '-' + settings[size];
43510             }
43511         });
43512         
43513         return cfg;
43514     },
43515     
43516     initEvents : function()
43517     {
43518         this.indicator = this.indicatorEl();
43519         
43520         this.initCurrencyEvent();
43521         
43522         this.initNumberEvent();
43523     },
43524     
43525     initCurrencyEvent : function()
43526     {
43527         if (!this.store) {
43528             throw "can not find store for combo";
43529         }
43530         
43531         this.store = Roo.factory(this.store, Roo.data);
43532         this.store.parent = this;
43533         
43534         this.createList();
43535         
43536         this.triggerEl = this.el.select('.input-group-addon', true).first();
43537         
43538         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43539         
43540         var _this = this;
43541         
43542         (function(){
43543             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43544             _this.list.setWidth(lw);
43545         }).defer(100);
43546         
43547         this.list.on('mouseover', this.onViewOver, this);
43548         this.list.on('mousemove', this.onViewMove, this);
43549         this.list.on('scroll', this.onViewScroll, this);
43550         
43551         if(!this.tpl){
43552             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43553         }
43554         
43555         this.view = new Roo.View(this.list, this.tpl, {
43556             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43557         });
43558         
43559         this.view.on('click', this.onViewClick, this);
43560         
43561         this.store.on('beforeload', this.onBeforeLoad, this);
43562         this.store.on('load', this.onLoad, this);
43563         this.store.on('loadexception', this.onLoadException, this);
43564         
43565         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43566             "up" : function(e){
43567                 this.inKeyMode = true;
43568                 this.selectPrev();
43569             },
43570
43571             "down" : function(e){
43572                 if(!this.isExpanded()){
43573                     this.onTriggerClick();
43574                 }else{
43575                     this.inKeyMode = true;
43576                     this.selectNext();
43577                 }
43578             },
43579
43580             "enter" : function(e){
43581                 this.collapse();
43582                 
43583                 if(this.fireEvent("specialkey", this, e)){
43584                     this.onViewClick(false);
43585                 }
43586                 
43587                 return true;
43588             },
43589
43590             "esc" : function(e){
43591                 this.collapse();
43592             },
43593
43594             "tab" : function(e){
43595                 this.collapse();
43596                 
43597                 if(this.fireEvent("specialkey", this, e)){
43598                     this.onViewClick(false);
43599                 }
43600                 
43601                 return true;
43602             },
43603
43604             scope : this,
43605
43606             doRelay : function(foo, bar, hname){
43607                 if(hname == 'down' || this.scope.isExpanded()){
43608                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43609                 }
43610                 return true;
43611             },
43612
43613             forceKeyDown: true
43614         });
43615         
43616         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43617         
43618     },
43619     
43620     initNumberEvent : function(e)
43621     {
43622         this.inputEl().on("keydown" , this.fireKey,  this);
43623         this.inputEl().on("focus", this.onFocus,  this);
43624         this.inputEl().on("blur", this.onBlur,  this);
43625         
43626         this.inputEl().relayEvent('keyup', this);
43627         
43628         if(this.indicator){
43629             this.indicator.addClass('invisible');
43630         }
43631  
43632         this.originalValue = this.getValue();
43633         
43634         if(this.validationEvent == 'keyup'){
43635             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43636             this.inputEl().on('keyup', this.filterValidation, this);
43637         }
43638         else if(this.validationEvent !== false){
43639             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43640         }
43641         
43642         if(this.selectOnFocus){
43643             this.on("focus", this.preFocus, this);
43644             
43645         }
43646         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43647             this.inputEl().on("keypress", this.filterKeys, this);
43648         } else {
43649             this.inputEl().relayEvent('keypress', this);
43650         }
43651         
43652         var allowed = "0123456789";
43653         
43654         if(this.allowDecimals){
43655             allowed += this.decimalSeparator;
43656         }
43657         
43658         if(this.allowNegative){
43659             allowed += "-";
43660         }
43661         
43662         if(this.thousandsDelimiter) {
43663             allowed += ",";
43664         }
43665         
43666         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43667         
43668         var keyPress = function(e){
43669             
43670             var k = e.getKey();
43671             
43672             var c = e.getCharCode();
43673             
43674             if(
43675                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43676                     allowed.indexOf(String.fromCharCode(c)) === -1
43677             ){
43678                 e.stopEvent();
43679                 return;
43680             }
43681             
43682             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43683                 return;
43684             }
43685             
43686             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43687                 e.stopEvent();
43688             }
43689         };
43690         
43691         this.inputEl().on("keypress", keyPress, this);
43692         
43693     },
43694     
43695     onTriggerClick : function(e)
43696     {   
43697         if(this.disabled){
43698             return;
43699         }
43700         
43701         this.page = 0;
43702         this.loadNext = false;
43703         
43704         if(this.isExpanded()){
43705             this.collapse();
43706             return;
43707         }
43708         
43709         this.hasFocus = true;
43710         
43711         if(this.triggerAction == 'all') {
43712             this.doQuery(this.allQuery, true);
43713             return;
43714         }
43715         
43716         this.doQuery(this.getRawValue());
43717     },
43718     
43719     getCurrency : function()
43720     {   
43721         var v = this.currencyEl().getValue();
43722         
43723         return v;
43724     },
43725     
43726     restrictHeight : function()
43727     {
43728         this.list.alignTo(this.currencyEl(), this.listAlign);
43729         this.list.alignTo(this.currencyEl(), this.listAlign);
43730     },
43731     
43732     onViewClick : function(view, doFocus, el, e)
43733     {
43734         var index = this.view.getSelectedIndexes()[0];
43735         
43736         var r = this.store.getAt(index);
43737         
43738         if(r){
43739             this.onSelect(r, index);
43740         }
43741     },
43742     
43743     onSelect : function(record, index){
43744         
43745         if(this.fireEvent('beforeselect', this, record, index) !== false){
43746         
43747             this.setFromCurrencyData(index > -1 ? record.data : false);
43748             
43749             this.collapse();
43750             
43751             this.fireEvent('select', this, record, index);
43752         }
43753     },
43754     
43755     setFromCurrencyData : function(o)
43756     {
43757         var currency = '';
43758         
43759         this.lastCurrency = o;
43760         
43761         if (this.currencyField) {
43762             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43763         } else {
43764             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43765         }
43766         
43767         this.lastSelectionText = currency;
43768         
43769         //setting default currency
43770         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43771             this.setCurrency(this.defaultCurrency);
43772             return;
43773         }
43774         
43775         this.setCurrency(currency);
43776     },
43777     
43778     setFromData : function(o)
43779     {
43780         var c = {};
43781         
43782         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43783         
43784         this.setFromCurrencyData(c);
43785         
43786         var value = '';
43787         
43788         if (this.name) {
43789             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43790         } else {
43791             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43792         }
43793         
43794         this.setValue(value);
43795         
43796     },
43797     
43798     setCurrency : function(v)
43799     {   
43800         this.currencyValue = v;
43801         
43802         if(this.rendered){
43803             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43804             this.validate();
43805         }
43806     },
43807     
43808     setValue : function(v)
43809     {
43810         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43811         
43812         this.value = v;
43813         
43814         if(this.rendered){
43815             
43816             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43817             
43818             this.inputEl().dom.value = (v == '') ? '' :
43819                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43820             
43821             if(!this.allowZero && v === '0') {
43822                 this.hiddenEl().dom.value = '';
43823                 this.inputEl().dom.value = '';
43824             }
43825             
43826             this.validate();
43827         }
43828     },
43829     
43830     getRawValue : function()
43831     {
43832         var v = this.inputEl().getValue();
43833         
43834         return v;
43835     },
43836     
43837     getValue : function()
43838     {
43839         return this.fixPrecision(this.parseValue(this.getRawValue()));
43840     },
43841     
43842     parseValue : function(value)
43843     {
43844         if(this.thousandsDelimiter) {
43845             value += "";
43846             r = new RegExp(",", "g");
43847             value = value.replace(r, "");
43848         }
43849         
43850         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43851         return isNaN(value) ? '' : value;
43852         
43853     },
43854     
43855     fixPrecision : function(value)
43856     {
43857         if(this.thousandsDelimiter) {
43858             value += "";
43859             r = new RegExp(",", "g");
43860             value = value.replace(r, "");
43861         }
43862         
43863         var nan = isNaN(value);
43864         
43865         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43866             return nan ? '' : value;
43867         }
43868         return parseFloat(value).toFixed(this.decimalPrecision);
43869     },
43870     
43871     decimalPrecisionFcn : function(v)
43872     {
43873         return Math.floor(v);
43874     },
43875     
43876     validateValue : function(value)
43877     {
43878         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43879             return false;
43880         }
43881         
43882         var num = this.parseValue(value);
43883         
43884         if(isNaN(num)){
43885             this.markInvalid(String.format(this.nanText, value));
43886             return false;
43887         }
43888         
43889         if(num < this.minValue){
43890             this.markInvalid(String.format(this.minText, this.minValue));
43891             return false;
43892         }
43893         
43894         if(num > this.maxValue){
43895             this.markInvalid(String.format(this.maxText, this.maxValue));
43896             return false;
43897         }
43898         
43899         return true;
43900     },
43901     
43902     validate : function()
43903     {
43904         if(this.disabled || this.allowBlank){
43905             this.markValid();
43906             return true;
43907         }
43908         
43909         var currency = this.getCurrency();
43910         
43911         if(this.validateValue(this.getRawValue()) && currency.length){
43912             this.markValid();
43913             return true;
43914         }
43915         
43916         this.markInvalid();
43917         return false;
43918     },
43919     
43920     getName: function()
43921     {
43922         return this.name;
43923     },
43924     
43925     beforeBlur : function()
43926     {
43927         if(!this.castInt){
43928             return;
43929         }
43930         
43931         var v = this.parseValue(this.getRawValue());
43932         
43933         if(v || v == 0){
43934             this.setValue(v);
43935         }
43936     },
43937     
43938     onBlur : function()
43939     {
43940         this.beforeBlur();
43941         
43942         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43943             //this.el.removeClass(this.focusClass);
43944         }
43945         
43946         this.hasFocus = false;
43947         
43948         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43949             this.validate();
43950         }
43951         
43952         var v = this.getValue();
43953         
43954         if(String(v) !== String(this.startValue)){
43955             this.fireEvent('change', this, v, this.startValue);
43956         }
43957         
43958         this.fireEvent("blur", this);
43959     },
43960     
43961     inputEl : function()
43962     {
43963         return this.el.select('.roo-money-amount-input', true).first();
43964     },
43965     
43966     currencyEl : function()
43967     {
43968         return this.el.select('.roo-money-currency-input', true).first();
43969     },
43970     
43971     hiddenEl : function()
43972     {
43973         return this.el.select('input.hidden-number-input',true).first();
43974     }
43975     
43976 });/**
43977  * @class Roo.bootstrap.BezierSignature
43978  * @extends Roo.bootstrap.Component
43979  * Bootstrap BezierSignature class
43980  * This script refer to:
43981  *    Title: Signature Pad
43982  *    Author: szimek
43983  *    Availability: https://github.com/szimek/signature_pad
43984  *
43985  * @constructor
43986  * Create a new BezierSignature
43987  * @param {Object} config The config object
43988  */
43989
43990 Roo.bootstrap.BezierSignature = function(config){
43991     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43992     this.addEvents({
43993         "resize" : true
43994     });
43995 };
43996
43997 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43998 {
43999      
44000     curve_data: [],
44001     
44002     is_empty: true,
44003     
44004     mouse_btn_down: true,
44005     
44006     /**
44007      * @cfg {int} canvas height
44008      */
44009     canvas_height: '200px',
44010     
44011     /**
44012      * @cfg {float|function} Radius of a single dot.
44013      */ 
44014     dot_size: false,
44015     
44016     /**
44017      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44018      */
44019     min_width: 0.5,
44020     
44021     /**
44022      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44023      */
44024     max_width: 2.5,
44025     
44026     /**
44027      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44028      */
44029     throttle: 16,
44030     
44031     /**
44032      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44033      */
44034     min_distance: 5,
44035     
44036     /**
44037      * @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.
44038      */
44039     bg_color: 'rgba(0, 0, 0, 0)',
44040     
44041     /**
44042      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44043      */
44044     dot_color: 'black',
44045     
44046     /**
44047      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44048      */ 
44049     velocity_filter_weight: 0.7,
44050     
44051     /**
44052      * @cfg {function} Callback when stroke begin. 
44053      */
44054     onBegin: false,
44055     
44056     /**
44057      * @cfg {function} Callback when stroke end.
44058      */
44059     onEnd: false,
44060     
44061     getAutoCreate : function()
44062     {
44063         var cls = 'roo-signature column';
44064         
44065         if(this.cls){
44066             cls += ' ' + this.cls;
44067         }
44068         
44069         var col_sizes = [
44070             'lg',
44071             'md',
44072             'sm',
44073             'xs'
44074         ];
44075         
44076         for(var i = 0; i < col_sizes.length; i++) {
44077             if(this[col_sizes[i]]) {
44078                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44079             }
44080         }
44081         
44082         var cfg = {
44083             tag: 'div',
44084             cls: cls,
44085             cn: [
44086                 {
44087                     tag: 'div',
44088                     cls: 'roo-signature-body',
44089                     cn: [
44090                         {
44091                             tag: 'canvas',
44092                             cls: 'roo-signature-body-canvas',
44093                             height: this.canvas_height,
44094                             width: this.canvas_width
44095                         }
44096                     ]
44097                 },
44098                 {
44099                     tag: 'input',
44100                     type: 'file',
44101                     style: 'display: none'
44102                 }
44103             ]
44104         };
44105         
44106         return cfg;
44107     },
44108     
44109     initEvents: function() 
44110     {
44111         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44112         
44113         var canvas = this.canvasEl();
44114         
44115         // mouse && touch event swapping...
44116         canvas.dom.style.touchAction = 'none';
44117         canvas.dom.style.msTouchAction = 'none';
44118         
44119         this.mouse_btn_down = false;
44120         canvas.on('mousedown', this._handleMouseDown, this);
44121         canvas.on('mousemove', this._handleMouseMove, this);
44122         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44123         
44124         if (window.PointerEvent) {
44125             canvas.on('pointerdown', this._handleMouseDown, this);
44126             canvas.on('pointermove', this._handleMouseMove, this);
44127             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44128         }
44129         
44130         if ('ontouchstart' in window) {
44131             canvas.on('touchstart', this._handleTouchStart, this);
44132             canvas.on('touchmove', this._handleTouchMove, this);
44133             canvas.on('touchend', this._handleTouchEnd, this);
44134         }
44135         
44136         Roo.EventManager.onWindowResize(this.resize, this, true);
44137         
44138         // file input event
44139         this.fileEl().on('change', this.uploadImage, this);
44140         
44141         this.clear();
44142         
44143         this.resize();
44144     },
44145     
44146     resize: function(){
44147         
44148         var canvas = this.canvasEl().dom;
44149         var ctx = this.canvasElCtx();
44150         var img_data = false;
44151         
44152         if(canvas.width > 0) {
44153             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44154         }
44155         // setting canvas width will clean img data
44156         canvas.width = 0;
44157         
44158         var style = window.getComputedStyle ? 
44159             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44160             
44161         var padding_left = parseInt(style.paddingLeft) || 0;
44162         var padding_right = parseInt(style.paddingRight) || 0;
44163         
44164         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44165         
44166         if(img_data) {
44167             ctx.putImageData(img_data, 0, 0);
44168         }
44169     },
44170     
44171     _handleMouseDown: function(e)
44172     {
44173         if (e.browserEvent.which === 1) {
44174             this.mouse_btn_down = true;
44175             this.strokeBegin(e);
44176         }
44177     },
44178     
44179     _handleMouseMove: function (e)
44180     {
44181         if (this.mouse_btn_down) {
44182             this.strokeMoveUpdate(e);
44183         }
44184     },
44185     
44186     _handleMouseUp: function (e)
44187     {
44188         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44189             this.mouse_btn_down = false;
44190             this.strokeEnd(e);
44191         }
44192     },
44193     
44194     _handleTouchStart: function (e) {
44195         
44196         e.preventDefault();
44197         if (e.browserEvent.targetTouches.length === 1) {
44198             // var touch = e.browserEvent.changedTouches[0];
44199             // this.strokeBegin(touch);
44200             
44201              this.strokeBegin(e); // assume e catching the correct xy...
44202         }
44203     },
44204     
44205     _handleTouchMove: function (e) {
44206         e.preventDefault();
44207         // var touch = event.targetTouches[0];
44208         // _this._strokeMoveUpdate(touch);
44209         this.strokeMoveUpdate(e);
44210     },
44211     
44212     _handleTouchEnd: function (e) {
44213         var wasCanvasTouched = e.target === this.canvasEl().dom;
44214         if (wasCanvasTouched) {
44215             e.preventDefault();
44216             // var touch = event.changedTouches[0];
44217             // _this._strokeEnd(touch);
44218             this.strokeEnd(e);
44219         }
44220     },
44221     
44222     reset: function () {
44223         this._lastPoints = [];
44224         this._lastVelocity = 0;
44225         this._lastWidth = (this.min_width + this.max_width) / 2;
44226         this.canvasElCtx().fillStyle = this.dot_color;
44227     },
44228     
44229     strokeMoveUpdate: function(e)
44230     {
44231         this.strokeUpdate(e);
44232         
44233         if (this.throttle) {
44234             this.throttleStroke(this.strokeUpdate, this.throttle);
44235         }
44236         else {
44237             this.strokeUpdate(e);
44238         }
44239     },
44240     
44241     strokeBegin: function(e)
44242     {
44243         var newPointGroup = {
44244             color: this.dot_color,
44245             points: []
44246         };
44247         
44248         if (typeof this.onBegin === 'function') {
44249             this.onBegin(e);
44250         }
44251         
44252         this.curve_data.push(newPointGroup);
44253         this.reset();
44254         this.strokeUpdate(e);
44255     },
44256     
44257     strokeUpdate: function(e)
44258     {
44259         var rect = this.canvasEl().dom.getBoundingClientRect();
44260         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44261         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44262         var lastPoints = lastPointGroup.points;
44263         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44264         var isLastPointTooClose = lastPoint
44265             ? point.distanceTo(lastPoint) <= this.min_distance
44266             : false;
44267         var color = lastPointGroup.color;
44268         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44269             var curve = this.addPoint(point);
44270             if (!lastPoint) {
44271                 this.drawDot({color: color, point: point});
44272             }
44273             else if (curve) {
44274                 this.drawCurve({color: color, curve: curve});
44275             }
44276             lastPoints.push({
44277                 time: point.time,
44278                 x: point.x,
44279                 y: point.y
44280             });
44281         }
44282     },
44283     
44284     strokeEnd: function(e)
44285     {
44286         this.strokeUpdate(e);
44287         if (typeof this.onEnd === 'function') {
44288             this.onEnd(e);
44289         }
44290     },
44291     
44292     addPoint:  function (point) {
44293         var _lastPoints = this._lastPoints;
44294         _lastPoints.push(point);
44295         if (_lastPoints.length > 2) {
44296             if (_lastPoints.length === 3) {
44297                 _lastPoints.unshift(_lastPoints[0]);
44298             }
44299             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44300             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44301             _lastPoints.shift();
44302             return curve;
44303         }
44304         return null;
44305     },
44306     
44307     calculateCurveWidths: function (startPoint, endPoint) {
44308         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44309             (1 - this.velocity_filter_weight) * this._lastVelocity;
44310
44311         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44312         var widths = {
44313             end: newWidth,
44314             start: this._lastWidth
44315         };
44316         
44317         this._lastVelocity = velocity;
44318         this._lastWidth = newWidth;
44319         return widths;
44320     },
44321     
44322     drawDot: function (_a) {
44323         var color = _a.color, point = _a.point;
44324         var ctx = this.canvasElCtx();
44325         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44326         ctx.beginPath();
44327         this.drawCurveSegment(point.x, point.y, width);
44328         ctx.closePath();
44329         ctx.fillStyle = color;
44330         ctx.fill();
44331     },
44332     
44333     drawCurve: function (_a) {
44334         var color = _a.color, curve = _a.curve;
44335         var ctx = this.canvasElCtx();
44336         var widthDelta = curve.endWidth - curve.startWidth;
44337         var drawSteps = Math.floor(curve.length()) * 2;
44338         ctx.beginPath();
44339         ctx.fillStyle = color;
44340         for (var i = 0; i < drawSteps; i += 1) {
44341         var t = i / drawSteps;
44342         var tt = t * t;
44343         var ttt = tt * t;
44344         var u = 1 - t;
44345         var uu = u * u;
44346         var uuu = uu * u;
44347         var x = uuu * curve.startPoint.x;
44348         x += 3 * uu * t * curve.control1.x;
44349         x += 3 * u * tt * curve.control2.x;
44350         x += ttt * curve.endPoint.x;
44351         var y = uuu * curve.startPoint.y;
44352         y += 3 * uu * t * curve.control1.y;
44353         y += 3 * u * tt * curve.control2.y;
44354         y += ttt * curve.endPoint.y;
44355         var width = curve.startWidth + ttt * widthDelta;
44356         this.drawCurveSegment(x, y, width);
44357         }
44358         ctx.closePath();
44359         ctx.fill();
44360     },
44361     
44362     drawCurveSegment: function (x, y, width) {
44363         var ctx = this.canvasElCtx();
44364         ctx.moveTo(x, y);
44365         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44366         this.is_empty = false;
44367     },
44368     
44369     clear: function()
44370     {
44371         var ctx = this.canvasElCtx();
44372         var canvas = this.canvasEl().dom;
44373         ctx.fillStyle = this.bg_color;
44374         ctx.clearRect(0, 0, canvas.width, canvas.height);
44375         ctx.fillRect(0, 0, canvas.width, canvas.height);
44376         this.curve_data = [];
44377         this.reset();
44378         this.is_empty = true;
44379     },
44380     
44381     fileEl: function()
44382     {
44383         return  this.el.select('input',true).first();
44384     },
44385     
44386     canvasEl: function()
44387     {
44388         return this.el.select('canvas',true).first();
44389     },
44390     
44391     canvasElCtx: function()
44392     {
44393         return this.el.select('canvas',true).first().dom.getContext('2d');
44394     },
44395     
44396     getImage: function(type)
44397     {
44398         if(this.is_empty) {
44399             return false;
44400         }
44401         
44402         // encryption ?
44403         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44404     },
44405     
44406     drawFromImage: function(img_src)
44407     {
44408         var img = new Image();
44409         
44410         img.onload = function(){
44411             this.canvasElCtx().drawImage(img, 0, 0);
44412         }.bind(this);
44413         
44414         img.src = img_src;
44415         
44416         this.is_empty = false;
44417     },
44418     
44419     selectImage: function()
44420     {
44421         this.fileEl().dom.click();
44422     },
44423     
44424     uploadImage: function(e)
44425     {
44426         var reader = new FileReader();
44427         
44428         reader.onload = function(e){
44429             var img = new Image();
44430             img.onload = function(){
44431                 this.reset();
44432                 this.canvasElCtx().drawImage(img, 0, 0);
44433             }.bind(this);
44434             img.src = e.target.result;
44435         }.bind(this);
44436         
44437         reader.readAsDataURL(e.target.files[0]);
44438     },
44439     
44440     // Bezier Point Constructor
44441     Point: (function () {
44442         function Point(x, y, time) {
44443             this.x = x;
44444             this.y = y;
44445             this.time = time || Date.now();
44446         }
44447         Point.prototype.distanceTo = function (start) {
44448             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44449         };
44450         Point.prototype.equals = function (other) {
44451             return this.x === other.x && this.y === other.y && this.time === other.time;
44452         };
44453         Point.prototype.velocityFrom = function (start) {
44454             return this.time !== start.time
44455             ? this.distanceTo(start) / (this.time - start.time)
44456             : 0;
44457         };
44458         return Point;
44459     }()),
44460     
44461     
44462     // Bezier Constructor
44463     Bezier: (function () {
44464         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44465             this.startPoint = startPoint;
44466             this.control2 = control2;
44467             this.control1 = control1;
44468             this.endPoint = endPoint;
44469             this.startWidth = startWidth;
44470             this.endWidth = endWidth;
44471         }
44472         Bezier.fromPoints = function (points, widths, scope) {
44473             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44474             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44475             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44476         };
44477         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44478             var dx1 = s1.x - s2.x;
44479             var dy1 = s1.y - s2.y;
44480             var dx2 = s2.x - s3.x;
44481             var dy2 = s2.y - s3.y;
44482             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44483             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44484             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44485             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44486             var dxm = m1.x - m2.x;
44487             var dym = m1.y - m2.y;
44488             var k = l2 / (l1 + l2);
44489             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44490             var tx = s2.x - cm.x;
44491             var ty = s2.y - cm.y;
44492             return {
44493                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44494                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44495             };
44496         };
44497         Bezier.prototype.length = function () {
44498             var steps = 10;
44499             var length = 0;
44500             var px;
44501             var py;
44502             for (var i = 0; i <= steps; i += 1) {
44503                 var t = i / steps;
44504                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44505                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44506                 if (i > 0) {
44507                     var xdiff = cx - px;
44508                     var ydiff = cy - py;
44509                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44510                 }
44511                 px = cx;
44512                 py = cy;
44513             }
44514             return length;
44515         };
44516         Bezier.prototype.point = function (t, start, c1, c2, end) {
44517             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44518             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44519             + (3.0 * c2 * (1.0 - t) * t * t)
44520             + (end * t * t * t);
44521         };
44522         return Bezier;
44523     }()),
44524     
44525     throttleStroke: function(fn, wait) {
44526       if (wait === void 0) { wait = 250; }
44527       var previous = 0;
44528       var timeout = null;
44529       var result;
44530       var storedContext;
44531       var storedArgs;
44532       var later = function () {
44533           previous = Date.now();
44534           timeout = null;
44535           result = fn.apply(storedContext, storedArgs);
44536           if (!timeout) {
44537               storedContext = null;
44538               storedArgs = [];
44539           }
44540       };
44541       return function wrapper() {
44542           var args = [];
44543           for (var _i = 0; _i < arguments.length; _i++) {
44544               args[_i] = arguments[_i];
44545           }
44546           var now = Date.now();
44547           var remaining = wait - (now - previous);
44548           storedContext = this;
44549           storedArgs = args;
44550           if (remaining <= 0 || remaining > wait) {
44551               if (timeout) {
44552                   clearTimeout(timeout);
44553                   timeout = null;
44554               }
44555               previous = now;
44556               result = fn.apply(storedContext, storedArgs);
44557               if (!timeout) {
44558                   storedContext = null;
44559                   storedArgs = [];
44560               }
44561           }
44562           else if (!timeout) {
44563               timeout = window.setTimeout(later, remaining);
44564           }
44565           return result;
44566       };
44567   }
44568   
44569 });
44570
44571  
44572
44573