roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     header_imageEl : false,
2079     
2080     layoutCls : function()
2081     {
2082         var cls = '';
2083         var t = this;
2084         Roo.log(this.margin_bottom.length);
2085         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2086             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2087             
2088             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2089                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2090             }
2091             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2092                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2093             }
2094         });
2095         
2096         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2097             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2098                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2099             }
2100         });
2101         
2102         // more generic support?
2103         if (this.hidden) {
2104             cls += ' d-none';
2105         }
2106         
2107         return cls;
2108     },
2109  
2110        // Roo.log("Call onRender: " + this.xtype);
2111         /*  We are looking at something like this.
2112 <div class="card">
2113     <img src="..." class="card-img-top" alt="...">
2114     <div class="card-body">
2115         <h5 class="card-title">Card title</h5>
2116          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2117
2118         >> this bit is really the body...
2119         <div> << we will ad dthis in hopefully it will not break shit.
2120         
2121         ** card text does not actually have any styling...
2122         
2123             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2124         
2125         </div> <<
2126           <a href="#" class="card-link">Card link</a>
2127           
2128     </div>
2129     <div class="card-footer">
2130         <small class="text-muted">Last updated 3 mins ago</small>
2131     </div>
2132 </div>
2133          */
2134     getAutoCreate : function(){
2135         
2136         var cfg = {
2137             tag : 'div',
2138             cls : 'card',
2139             cn : [ ]
2140         };
2141         
2142         if (this.weight.length && this.weight != 'light') {
2143             cfg.cls += ' text-white';
2144         } else {
2145             cfg.cls += ' text-dark'; // need as it's nested..
2146         }
2147         if (this.weight.length) {
2148             cfg.cls += ' bg-' + this.weight;
2149         }
2150         
2151         cfg.cls += ' ' + this.layoutCls(); 
2152         
2153         var hdr = false;
2154         var hdr_ctr = false;
2155         if (this.header.length) {
2156             hdr = {
2157                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2158                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2159                 cn : []
2160             };
2161             cfg.cn.push(hdr);
2162             hdr_ctr = hdr;
2163         } else {
2164             hdr = {
2165                 tag : 'div',
2166                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2167                 cn : []
2168             };
2169             cfg.cn.push(hdr);
2170             hdr_ctr = hdr;
2171         }
2172         if (this.collapsable) {
2173             hdr_ctr = {
2174             tag : 'a',
2175             cls : 'd-block user-select-none',
2176             cn: [
2177                     {
2178                         tag: 'i',
2179                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2180                     }
2181                    
2182                 ]
2183             };
2184             hdr.cn.push(hdr_ctr);
2185         }
2186         
2187         hdr_ctr.cn.push(        {
2188             tag: 'span',
2189             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2190             html : this.header
2191         });
2192         
2193         
2194         if (this.header_image.length) {
2195             cfg.cn.push({
2196                 tag : 'img',
2197                 cls : 'card-img-top',
2198                 src: this.header_image // escape?
2199             });
2200         } else {
2201             cfg.cn.push({
2202                     tag : 'div',
2203                     cls : 'card-img-top d-none' 
2204                 });
2205         }
2206             
2207         var body = {
2208             tag : 'div',
2209             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2210             cn : []
2211         };
2212         var obody = body;
2213         if (this.collapsable || this.rotateable) {
2214             obody = {
2215                 tag: 'div',
2216                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2217                 cn : [  body ]
2218             };
2219         }
2220         
2221         cfg.cn.push(obody);
2222         
2223         if (this.title.length) {
2224             body.cn.push({
2225                 tag : 'div',
2226                 cls : 'card-title',
2227                 src: this.title // escape?
2228             });
2229         }  
2230         
2231         if (this.subtitle.length) {
2232             body.cn.push({
2233                 tag : 'div',
2234                 cls : 'card-title',
2235                 src: this.subtitle // escape?
2236             });
2237         }
2238         
2239         body.cn.push({
2240             tag : 'div',
2241             cls : 'roo-card-body-ctr'
2242         });
2243         
2244         if (this.html.length) {
2245             body.cn.push({
2246                 tag: 'div',
2247                 html : this.html
2248             });
2249         }
2250         // fixme ? handle objects?
2251         
2252         if (this.footer.length) {
2253            
2254             cfg.cn.push({
2255                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2256                 html : this.footer
2257             });
2258             
2259         } else {
2260             cfg.cn.push({cls : 'card-footer d-none'});
2261         }
2262         
2263         // footer...
2264         
2265         return cfg;
2266     },
2267     
2268     
2269     getCardHeader : function()
2270     {
2271         var  ret = this.el.select('.card-header',true).first();
2272         if (ret.hasClass('d-none')) {
2273             ret.removeClass('d-none');
2274         }
2275         
2276         return ret;
2277     },
2278     getCardFooter : function()
2279     {
2280         var  ret = this.el.select('.card-footer',true).first();
2281         if (ret.hasClass('d-none')) {
2282             ret.removeClass('d-none');
2283         }
2284         
2285         return ret;
2286     },
2287     getCardImageTop : function()
2288     {
2289         var  ret = this.header_imageEl;
2290         if (ret.hasClass('d-none')) {
2291             ret.removeClass('d-none');
2292         }
2293             
2294         return ret;
2295     },
2296     
2297     getChildContainer : function()
2298     {
2299         
2300         if(!this.el){
2301             return false;
2302         }
2303         return this.el.select('.roo-card-body-ctr',true).first();    
2304     },
2305     
2306     initEvents: function() 
2307     {
2308         this.bodyEl = this.el.select('.card-body',true).first(); 
2309         this.containerEl = this.getChildContainer();
2310         if(this.dragable){
2311             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2312                     containerScroll: true,
2313                     ddGroup: this.drag_group || 'default_card_drag_group'
2314             });
2315             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2316         }
2317         if (this.dropable) {
2318             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2319                 containerScroll: true,
2320                 ddGroup: this.drop_group || 'default_card_drag_group'
2321             });
2322             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2323             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2324             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2325             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2326             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2327         }
2328         
2329         if (this.collapsable) {
2330             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2331         }
2332         if (this.rotateable) {
2333             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2334         }
2335         this.collapsableEl = this.el.select('.roo-collapsable').first();
2336          
2337         this.footerEl = this.el.select('.card-footer').first();
2338         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2339         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2340         this.headerEl = this.el.select('.card-header',true).first();
2341         
2342         if (this.rotated) {
2343             this.el.addClass('roo-card-rotated');
2344             this.fireEvent('rotate', this, true);
2345         }
2346         this.header_imageEl = this.el.select('.card-img-top',true).first(); 
2347         this.header_imageEl.on('load', this.onHeaderImageLoad, this );
2348         
2349     },
2350     getDragData : function(e)
2351     {
2352         var target = this.getEl();
2353         if (target) {
2354             //this.handleSelection(e);
2355             
2356             var dragData = {
2357                 source: this,
2358                 copy: false,
2359                 nodes: this.getEl(),
2360                 records: []
2361             };
2362             
2363             
2364             dragData.ddel = target.dom ;    // the div element
2365             Roo.log(target.getWidth( ));
2366             dragData.ddel.style.width = target.getWidth() + 'px';
2367             
2368             return dragData;
2369         }
2370         return false;
2371     },
2372     /**
2373     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2374     *    whole Element becomes the target, and this causes the drop gesture to append.
2375     */
2376     getTargetFromEvent : function(e, dragged_card_el)
2377     {
2378         var target = e.getTarget();
2379         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2380             target = target.parentNode;
2381         }
2382         
2383         var ret = {
2384             position: '',
2385             cards : [],
2386             card_n : -1,
2387             items_n : -1,
2388             card : false 
2389         };
2390         
2391         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2392         // see if target is one of the 'cards'...
2393         
2394         
2395         //Roo.log(this.items.length);
2396         var pos = false;
2397         
2398         var last_card_n = 0;
2399         var cards_len  = 0;
2400         for (var i = 0;i< this.items.length;i++) {
2401             
2402             if (!this.items[i].el.hasClass('card')) {
2403                  continue;
2404             }
2405             pos = this.getDropPoint(e, this.items[i].el.dom);
2406             
2407             cards_len = ret.cards.length;
2408             //Roo.log(this.items[i].el.dom.id);
2409             ret.cards.push(this.items[i]);
2410             last_card_n  = i;
2411             if (ret.card_n < 0 && pos == 'above') {
2412                 ret.position = cards_len > 0 ? 'below' : pos;
2413                 ret.items_n = i > 0 ? i - 1 : 0;
2414                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2415                 ret.card = ret.cards[ret.card_n];
2416             }
2417         }
2418         if (!ret.cards.length) {
2419             ret.card = true;
2420             ret.position = 'below';
2421             ret.items_n;
2422             return ret;
2423         }
2424         // could not find a card.. stick it at the end..
2425         if (ret.card_n < 0) {
2426             ret.card_n = last_card_n;
2427             ret.card = ret.cards[last_card_n];
2428             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2429             ret.position = 'below';
2430         }
2431         
2432         if (this.items[ret.items_n].el == dragged_card_el) {
2433             return false;
2434         }
2435         
2436         if (ret.position == 'below') {
2437             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2438             
2439             if (card_after  && card_after.el == dragged_card_el) {
2440                 return false;
2441             }
2442             return ret;
2443         }
2444         
2445         // its's after ..
2446         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2447         
2448         if (card_before  && card_before.el == dragged_card_el) {
2449             return false;
2450         }
2451         
2452         return ret;
2453     },
2454     
2455     onNodeEnter : function(n, dd, e, data){
2456         return false;
2457     },
2458     onNodeOver : function(n, dd, e, data)
2459     {
2460        
2461         var target_info = this.getTargetFromEvent(e,data.source.el);
2462         if (target_info === false) {
2463             this.dropPlaceHolder('hide');
2464             return false;
2465         }
2466         Roo.log(['getTargetFromEvent', target_info ]);
2467         
2468          
2469         this.dropPlaceHolder('show', target_info,data);
2470         
2471         return false; 
2472     },
2473     onNodeOut : function(n, dd, e, data){
2474         this.dropPlaceHolder('hide');
2475      
2476     },
2477     onNodeDrop : function(n, dd, e, data)
2478     {
2479         
2480         // call drop - return false if
2481         
2482         // this could actually fail - if the Network drops..
2483         // we will ignore this at present..- client should probably reload
2484         // the whole set of cards if stuff like that fails.
2485         
2486         
2487         var info = this.getTargetFromEvent(e,data.source.el);
2488         if (info === false) {
2489             return false;
2490         }
2491         this.dropPlaceHolder('hide');
2492   
2493          
2494     
2495     
2496     
2497         this.acceptCard(data.source, info.position, info.card, info.items_n);
2498         return true;
2499          
2500     },
2501     firstChildCard : function()
2502     {
2503         for (var i = 0;i< this.items.length;i++) {
2504             
2505             if (!this.items[i].el.hasClass('card')) {
2506                  continue;
2507             }
2508             return this.items[i];
2509         }
2510         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2511     },
2512     /**
2513      * accept card
2514      *
2515      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2516      */
2517     acceptCard : function(move_card,  position, next_to_card )
2518     {
2519         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2520             return false;
2521         }
2522         
2523         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2524         
2525         move_card.parent().removeCard(move_card);
2526         
2527         
2528         var dom = move_card.el.dom;
2529         dom.style.width = ''; // clear with - which is set by drag.
2530         
2531         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2532             var cardel = next_to_card.el.dom;
2533             
2534             if (position == 'above' ) {
2535                 cardel.parentNode.insertBefore(dom, cardel);
2536             } else if (cardel.nextSibling) {
2537                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2538             } else {
2539                 cardel.parentNode.append(dom);
2540             }
2541         } else {
2542             // card container???
2543             this.containerEl.dom.append(dom);
2544         }
2545         
2546         //FIXME HANDLE card = true 
2547         
2548         // add this to the correct place in items.
2549         
2550         // remove Card from items.
2551         
2552        
2553         if (this.items.length) {
2554             var nitems = [];
2555             //Roo.log([info.items_n, info.position, this.items.length]);
2556             for (var i =0; i < this.items.length; i++) {
2557                 if (i == to_items_n && position == 'above') {
2558                     nitems.push(move_card);
2559                 }
2560                 nitems.push(this.items[i]);
2561                 if (i == to_items_n && position == 'below') {
2562                     nitems.push(move_card);
2563                 }
2564             }
2565             this.items = nitems;
2566             Roo.log(this.items);
2567         } else {
2568             this.items.push(move_card);
2569         }
2570         
2571         move_card.parentId = this.id;
2572         
2573         return true;
2574         
2575         
2576     },
2577     removeCard : function(c)
2578     {
2579         this.items = this.items.filter(function(e) { return e != c });
2580  
2581         var dom = c.el.dom;
2582         dom.parentNode.removeChild(dom);
2583         dom.style.width = ''; // clear with - which is set by drag.
2584         c.parentId = false;
2585         
2586     },
2587     
2588     /**    Decide whether to drop above or below a View node. */
2589     getDropPoint : function(e, n, dd)
2590     {
2591         if (dd) {
2592              return false;
2593         }
2594         if (n == this.containerEl.dom) {
2595             return "above";
2596         }
2597         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2598         var c = t + (b - t) / 2;
2599         var y = Roo.lib.Event.getPageY(e);
2600         if(y <= c) {
2601             return "above";
2602         }else{
2603             return "below";
2604         }
2605     },
2606     onToggleCollapse : function(e)
2607         {
2608         if (this.collapsed) {
2609             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2610             this.collapsableEl.addClass('show');
2611             this.collapsed = false;
2612             return;
2613         }
2614         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2615         this.collapsableEl.removeClass('show');
2616         this.collapsed = true;
2617         
2618     
2619     },
2620     
2621     onToggleRotate : function(e)
2622     {
2623         this.collapsableEl.removeClass('show');
2624         this.footerEl.removeClass('d-none');
2625         this.el.removeClass('roo-card-rotated');
2626         this.el.removeClass('d-none');
2627         if (this.rotated) {
2628             
2629             this.collapsableEl.addClass('show');
2630             this.rotated = false;
2631             this.fireEvent('rotate', this, this.rotated);
2632             return;
2633         }
2634         this.el.addClass('roo-card-rotated');
2635         this.footerEl.addClass('d-none');
2636         this.el.select('.roo-collapsable').removeClass('show');
2637         
2638         this.rotated = true;
2639         this.fireEvent('rotate', this, this.rotated);
2640     
2641     },
2642     
2643     dropPlaceHolder: function (action, info, data)
2644     {
2645         if (this.dropEl === false) {
2646             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2647             cls : 'd-none'
2648             },true);
2649         }
2650         this.dropEl.removeClass(['d-none', 'd-block']);        
2651         if (action == 'hide') {
2652             
2653             this.dropEl.addClass('d-none');
2654             return;
2655         }
2656         // FIXME - info.card == true!!!
2657         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2658         
2659         if (info.card !== true) {
2660             var cardel = info.card.el.dom;
2661             
2662             if (info.position == 'above') {
2663                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2664             } else if (cardel.nextSibling) {
2665                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2666             } else {
2667                 cardel.parentNode.append(this.dropEl.dom);
2668             }
2669         } else {
2670             // card container???
2671             this.containerEl.dom.append(this.dropEl.dom);
2672         }
2673         
2674         this.dropEl.addClass('d-block roo-card-dropzone');
2675         
2676         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2677         
2678         
2679     
2680     
2681     
2682     },
2683     setHeaderText: function(html)
2684     {
2685         this.header = html;
2686         if (this.headerContainerEl) {
2687             this.headerContainerEl.dom.innerHTML = html;
2688         }
2689     },
2690     onHeaderImageLoad : function(ev, he)
2691     {
2692         if (!this.header_image_fit_square) {
2693             return;
2694         }
2695         
2696         var hw = he.naturalHeight / he.naturalWidth;
2697         // wide image = < 0
2698         // tall image = > 1
2699         //var w = he.dom.naturalWidth;
2700         var ww = he.width;
2701         he.style.left =  0;
2702         he.style.position =  'relative';
2703         if (hw > 1) {
2704             var nw = (ww * (1/hw));
2705             Roo.get(he).setSize( ww * (1/hw),  ww);
2706             he.style.left =  ((ww - nw)/ 2) + 'px';
2707             he.style.position =  'relative';
2708         }
2709
2710     }
2711
2712     
2713 });
2714
2715 /*
2716  * - LGPL
2717  *
2718  * Card header - holder for the card header elements.
2719  * 
2720  */
2721
2722 /**
2723  * @class Roo.bootstrap.CardHeader
2724  * @extends Roo.bootstrap.Element
2725  * Bootstrap CardHeader class
2726  * @constructor
2727  * Create a new Card Header - that you can embed children into
2728  * @param {Object} config The config object
2729  */
2730
2731 Roo.bootstrap.CardHeader = function(config){
2732     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2733 };
2734
2735 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2736     
2737     
2738     container_method : 'getCardHeader' 
2739     
2740      
2741     
2742     
2743    
2744 });
2745
2746  
2747
2748  /*
2749  * - LGPL
2750  *
2751  * Card footer - holder for the card footer elements.
2752  * 
2753  */
2754
2755 /**
2756  * @class Roo.bootstrap.CardFooter
2757  * @extends Roo.bootstrap.Element
2758  * Bootstrap CardFooter class
2759  * @constructor
2760  * Create a new Card Footer - that you can embed children into
2761  * @param {Object} config The config object
2762  */
2763
2764 Roo.bootstrap.CardFooter = function(config){
2765     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2766 };
2767
2768 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2769     
2770     
2771     container_method : 'getCardFooter' 
2772     
2773      
2774     
2775     
2776    
2777 });
2778
2779  
2780
2781  /*
2782  * - LGPL
2783  *
2784  * Card header - holder for the card header elements.
2785  * 
2786  */
2787
2788 /**
2789  * @class Roo.bootstrap.CardImageTop
2790  * @extends Roo.bootstrap.Element
2791  * Bootstrap CardImageTop class
2792  * @constructor
2793  * Create a new Card Image Top container
2794  * @param {Object} config The config object
2795  */
2796
2797 Roo.bootstrap.CardImageTop = function(config){
2798     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2799 };
2800
2801 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2802     
2803    
2804     container_method : 'getCardImageTop' 
2805     
2806      
2807     
2808    
2809 });
2810
2811  
2812
2813  /*
2814  * - LGPL
2815  *
2816  * image
2817  * 
2818  */
2819
2820
2821 /**
2822  * @class Roo.bootstrap.Img
2823  * @extends Roo.bootstrap.Component
2824  * Bootstrap Img class
2825  * @cfg {Boolean} imgResponsive false | true
2826  * @cfg {String} border rounded | circle | thumbnail
2827  * @cfg {String} src image source
2828  * @cfg {String} alt image alternative text
2829  * @cfg {String} href a tag href
2830  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2831  * @cfg {String} xsUrl xs image source
2832  * @cfg {String} smUrl sm image source
2833  * @cfg {String} mdUrl md image source
2834  * @cfg {String} lgUrl lg image source
2835  * 
2836  * @constructor
2837  * Create a new Input
2838  * @param {Object} config The config object
2839  */
2840
2841 Roo.bootstrap.Img = function(config){
2842     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2843     
2844     this.addEvents({
2845         // img events
2846         /**
2847          * @event click
2848          * The img click event for the img.
2849          * @param {Roo.EventObject} e
2850          */
2851         "click" : true
2852     });
2853 };
2854
2855 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2856     
2857     imgResponsive: true,
2858     border: '',
2859     src: 'about:blank',
2860     href: false,
2861     target: false,
2862     xsUrl: '',
2863     smUrl: '',
2864     mdUrl: '',
2865     lgUrl: '',
2866
2867     getAutoCreate : function()
2868     {   
2869         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2870             return this.createSingleImg();
2871         }
2872         
2873         var cfg = {
2874             tag: 'div',
2875             cls: 'roo-image-responsive-group',
2876             cn: []
2877         };
2878         var _this = this;
2879         
2880         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2881             
2882             if(!_this[size + 'Url']){
2883                 return;
2884             }
2885             
2886             var img = {
2887                 tag: 'img',
2888                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2889                 html: _this.html || cfg.html,
2890                 src: _this[size + 'Url']
2891             };
2892             
2893             img.cls += ' roo-image-responsive-' + size;
2894             
2895             var s = ['xs', 'sm', 'md', 'lg'];
2896             
2897             s.splice(s.indexOf(size), 1);
2898             
2899             Roo.each(s, function(ss){
2900                 img.cls += ' hidden-' + ss;
2901             });
2902             
2903             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2904                 cfg.cls += ' img-' + _this.border;
2905             }
2906             
2907             if(_this.alt){
2908                 cfg.alt = _this.alt;
2909             }
2910             
2911             if(_this.href){
2912                 var a = {
2913                     tag: 'a',
2914                     href: _this.href,
2915                     cn: [
2916                         img
2917                     ]
2918                 };
2919
2920                 if(this.target){
2921                     a.target = _this.target;
2922                 }
2923             }
2924             
2925             cfg.cn.push((_this.href) ? a : img);
2926             
2927         });
2928         
2929         return cfg;
2930     },
2931     
2932     createSingleImg : function()
2933     {
2934         var cfg = {
2935             tag: 'img',
2936             cls: (this.imgResponsive) ? 'img-responsive' : '',
2937             html : null,
2938             src : 'about:blank'  // just incase src get's set to undefined?!?
2939         };
2940         
2941         cfg.html = this.html || cfg.html;
2942         
2943         cfg.src = this.src || cfg.src;
2944         
2945         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2946             cfg.cls += ' img-' + this.border;
2947         }
2948         
2949         if(this.alt){
2950             cfg.alt = this.alt;
2951         }
2952         
2953         if(this.href){
2954             var a = {
2955                 tag: 'a',
2956                 href: this.href,
2957                 cn: [
2958                     cfg
2959                 ]
2960             };
2961             
2962             if(this.target){
2963                 a.target = this.target;
2964             }
2965             
2966         }
2967         
2968         return (this.href) ? a : cfg;
2969     },
2970     
2971     initEvents: function() 
2972     {
2973         if(!this.href){
2974             this.el.on('click', this.onClick, this);
2975         }
2976         
2977     },
2978     
2979     onClick : function(e)
2980     {
2981         Roo.log('img onclick');
2982         this.fireEvent('click', this, e);
2983     },
2984     /**
2985      * Sets the url of the image - used to update it
2986      * @param {String} url the url of the image
2987      */
2988     
2989     setSrc : function(url)
2990     {
2991         this.src =  url;
2992         
2993         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2994             this.el.dom.src =  url;
2995             return;
2996         }
2997         
2998         this.el.select('img', true).first().dom.src =  url;
2999     }
3000     
3001     
3002    
3003 });
3004
3005  /*
3006  * - LGPL
3007  *
3008  * image
3009  * 
3010  */
3011
3012
3013 /**
3014  * @class Roo.bootstrap.Link
3015  * @extends Roo.bootstrap.Component
3016  * Bootstrap Link Class
3017  * @cfg {String} alt image alternative text
3018  * @cfg {String} href a tag href
3019  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
3020  * @cfg {String} html the content of the link.
3021  * @cfg {String} anchor name for the anchor link
3022  * @cfg {String} fa - favicon
3023
3024  * @cfg {Boolean} preventDefault (true | false) default false
3025
3026  * 
3027  * @constructor
3028  * Create a new Input
3029  * @param {Object} config The config object
3030  */
3031
3032 Roo.bootstrap.Link = function(config){
3033     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3034     
3035     this.addEvents({
3036         // img events
3037         /**
3038          * @event click
3039          * The img click event for the img.
3040          * @param {Roo.EventObject} e
3041          */
3042         "click" : true
3043     });
3044 };
3045
3046 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3047     
3048     href: false,
3049     target: false,
3050     preventDefault: false,
3051     anchor : false,
3052     alt : false,
3053     fa: false,
3054
3055
3056     getAutoCreate : function()
3057     {
3058         var html = this.html || '';
3059         
3060         if (this.fa !== false) {
3061             html = '<i class="fa fa-' + this.fa + '"></i>';
3062         }
3063         var cfg = {
3064             tag: 'a'
3065         };
3066         // anchor's do not require html/href...
3067         if (this.anchor === false) {
3068             cfg.html = html;
3069             cfg.href = this.href || '#';
3070         } else {
3071             cfg.name = this.anchor;
3072             if (this.html !== false || this.fa !== false) {
3073                 cfg.html = html;
3074             }
3075             if (this.href !== false) {
3076                 cfg.href = this.href;
3077             }
3078         }
3079         
3080         if(this.alt !== false){
3081             cfg.alt = this.alt;
3082         }
3083         
3084         
3085         if(this.target !== false) {
3086             cfg.target = this.target;
3087         }
3088         
3089         return cfg;
3090     },
3091     
3092     initEvents: function() {
3093         
3094         if(!this.href || this.preventDefault){
3095             this.el.on('click', this.onClick, this);
3096         }
3097     },
3098     
3099     onClick : function(e)
3100     {
3101         if(this.preventDefault){
3102             e.preventDefault();
3103         }
3104         //Roo.log('img onclick');
3105         this.fireEvent('click', this, e);
3106     }
3107    
3108 });
3109
3110  /*
3111  * - LGPL
3112  *
3113  * header
3114  * 
3115  */
3116
3117 /**
3118  * @class Roo.bootstrap.Header
3119  * @extends Roo.bootstrap.Component
3120  * Bootstrap Header class
3121  * @cfg {String} html content of header
3122  * @cfg {Number} level (1|2|3|4|5|6) default 1
3123  * 
3124  * @constructor
3125  * Create a new Header
3126  * @param {Object} config The config object
3127  */
3128
3129
3130 Roo.bootstrap.Header  = function(config){
3131     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3132 };
3133
3134 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3135     
3136     //href : false,
3137     html : false,
3138     level : 1,
3139     
3140     
3141     
3142     getAutoCreate : function(){
3143         
3144         
3145         
3146         var cfg = {
3147             tag: 'h' + (1 *this.level),
3148             html: this.html || ''
3149         } ;
3150         
3151         return cfg;
3152     }
3153    
3154 });
3155
3156  
3157
3158  /*
3159  * Based on:
3160  * Ext JS Library 1.1.1
3161  * Copyright(c) 2006-2007, Ext JS, LLC.
3162  *
3163  * Originally Released Under LGPL - original licence link has changed is not relivant.
3164  *
3165  * Fork - LGPL
3166  * <script type="text/javascript">
3167  */
3168  
3169 /**
3170  * @class Roo.bootstrap.MenuMgr
3171  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3172  * @singleton
3173  */
3174 Roo.bootstrap.MenuMgr = function(){
3175    var menus, active, groups = {}, attached = false, lastShow = new Date();
3176
3177    // private - called when first menu is created
3178    function init(){
3179        menus = {};
3180        active = new Roo.util.MixedCollection();
3181        Roo.get(document).addKeyListener(27, function(){
3182            if(active.length > 0){
3183                hideAll();
3184            }
3185        });
3186    }
3187
3188    // private
3189    function hideAll(){
3190        if(active && active.length > 0){
3191            var c = active.clone();
3192            c.each(function(m){
3193                m.hide();
3194            });
3195        }
3196    }
3197
3198    // private
3199    function onHide(m){
3200        active.remove(m);
3201        if(active.length < 1){
3202            Roo.get(document).un("mouseup", onMouseDown);
3203             
3204            attached = false;
3205        }
3206    }
3207
3208    // private
3209    function onShow(m){
3210        var last = active.last();
3211        lastShow = new Date();
3212        active.add(m);
3213        if(!attached){
3214           Roo.get(document).on("mouseup", onMouseDown);
3215            
3216            attached = true;
3217        }
3218        if(m.parentMenu){
3219           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3220           m.parentMenu.activeChild = m;
3221        }else if(last && last.isVisible()){
3222           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3223        }
3224    }
3225
3226    // private
3227    function onBeforeHide(m){
3228        if(m.activeChild){
3229            m.activeChild.hide();
3230        }
3231        if(m.autoHideTimer){
3232            clearTimeout(m.autoHideTimer);
3233            delete m.autoHideTimer;
3234        }
3235    }
3236
3237    // private
3238    function onBeforeShow(m){
3239        var pm = m.parentMenu;
3240        if(!pm && !m.allowOtherMenus){
3241            hideAll();
3242        }else if(pm && pm.activeChild && active != m){
3243            pm.activeChild.hide();
3244        }
3245    }
3246
3247    // private this should really trigger on mouseup..
3248    function onMouseDown(e){
3249         Roo.log("on Mouse Up");
3250         
3251         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3252             Roo.log("MenuManager hideAll");
3253             hideAll();
3254             e.stopEvent();
3255         }
3256         
3257         
3258    }
3259
3260    // private
3261    function onBeforeCheck(mi, state){
3262        if(state){
3263            var g = groups[mi.group];
3264            for(var i = 0, l = g.length; i < l; i++){
3265                if(g[i] != mi){
3266                    g[i].setChecked(false);
3267                }
3268            }
3269        }
3270    }
3271
3272    return {
3273
3274        /**
3275         * Hides all menus that are currently visible
3276         */
3277        hideAll : function(){
3278             hideAll();  
3279        },
3280
3281        // private
3282        register : function(menu){
3283            if(!menus){
3284                init();
3285            }
3286            menus[menu.id] = menu;
3287            menu.on("beforehide", onBeforeHide);
3288            menu.on("hide", onHide);
3289            menu.on("beforeshow", onBeforeShow);
3290            menu.on("show", onShow);
3291            var g = menu.group;
3292            if(g && menu.events["checkchange"]){
3293                if(!groups[g]){
3294                    groups[g] = [];
3295                }
3296                groups[g].push(menu);
3297                menu.on("checkchange", onCheck);
3298            }
3299        },
3300
3301         /**
3302          * Returns a {@link Roo.menu.Menu} object
3303          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3304          * be used to generate and return a new Menu instance.
3305          */
3306        get : function(menu){
3307            if(typeof menu == "string"){ // menu id
3308                return menus[menu];
3309            }else if(menu.events){  // menu instance
3310                return menu;
3311            }
3312            /*else if(typeof menu.length == 'number'){ // array of menu items?
3313                return new Roo.bootstrap.Menu({items:menu});
3314            }else{ // otherwise, must be a config
3315                return new Roo.bootstrap.Menu(menu);
3316            }
3317            */
3318            return false;
3319        },
3320
3321        // private
3322        unregister : function(menu){
3323            delete menus[menu.id];
3324            menu.un("beforehide", onBeforeHide);
3325            menu.un("hide", onHide);
3326            menu.un("beforeshow", onBeforeShow);
3327            menu.un("show", onShow);
3328            var g = menu.group;
3329            if(g && menu.events["checkchange"]){
3330                groups[g].remove(menu);
3331                menu.un("checkchange", onCheck);
3332            }
3333        },
3334
3335        // private
3336        registerCheckable : function(menuItem){
3337            var g = menuItem.group;
3338            if(g){
3339                if(!groups[g]){
3340                    groups[g] = [];
3341                }
3342                groups[g].push(menuItem);
3343                menuItem.on("beforecheckchange", onBeforeCheck);
3344            }
3345        },
3346
3347        // private
3348        unregisterCheckable : function(menuItem){
3349            var g = menuItem.group;
3350            if(g){
3351                groups[g].remove(menuItem);
3352                menuItem.un("beforecheckchange", onBeforeCheck);
3353            }
3354        }
3355    };
3356 }();/*
3357  * - LGPL
3358  *
3359  * menu
3360  * 
3361  */
3362
3363 /**
3364  * @class Roo.bootstrap.Menu
3365  * @extends Roo.bootstrap.Component
3366  * Bootstrap Menu class - container for MenuItems
3367  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3368  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3369  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3370  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3371  * 
3372  * @constructor
3373  * Create a new Menu
3374  * @param {Object} config The config object
3375  */
3376
3377
3378 Roo.bootstrap.Menu = function(config){
3379     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3380     if (this.registerMenu && this.type != 'treeview')  {
3381         Roo.bootstrap.MenuMgr.register(this);
3382     }
3383     
3384     
3385     this.addEvents({
3386         /**
3387          * @event beforeshow
3388          * Fires before this menu is displayed (return false to block)
3389          * @param {Roo.menu.Menu} this
3390          */
3391         beforeshow : true,
3392         /**
3393          * @event beforehide
3394          * Fires before this menu is hidden (return false to block)
3395          * @param {Roo.menu.Menu} this
3396          */
3397         beforehide : true,
3398         /**
3399          * @event show
3400          * Fires after this menu is displayed
3401          * @param {Roo.menu.Menu} this
3402          */
3403         show : true,
3404         /**
3405          * @event hide
3406          * Fires after this menu is hidden
3407          * @param {Roo.menu.Menu} this
3408          */
3409         hide : true,
3410         /**
3411          * @event click
3412          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3413          * @param {Roo.menu.Menu} this
3414          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3415          * @param {Roo.EventObject} e
3416          */
3417         click : true,
3418         /**
3419          * @event mouseover
3420          * Fires when the mouse is hovering over this menu
3421          * @param {Roo.menu.Menu} this
3422          * @param {Roo.EventObject} e
3423          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3424          */
3425         mouseover : true,
3426         /**
3427          * @event mouseout
3428          * Fires when the mouse exits this menu
3429          * @param {Roo.menu.Menu} this
3430          * @param {Roo.EventObject} e
3431          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3432          */
3433         mouseout : true,
3434         /**
3435          * @event itemclick
3436          * Fires when a menu item contained in this menu is clicked
3437          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3438          * @param {Roo.EventObject} e
3439          */
3440         itemclick: true
3441     });
3442     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3443 };
3444
3445 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3446     
3447    /// html : false,
3448     //align : '',
3449     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3450     type: false,
3451     /**
3452      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3453      */
3454     registerMenu : true,
3455     
3456     menuItems :false, // stores the menu items..
3457     
3458     hidden:true,
3459         
3460     parentMenu : false,
3461     
3462     stopEvent : true,
3463     
3464     isLink : false,
3465     
3466     getChildContainer : function() {
3467         return this.el;  
3468     },
3469     
3470     getAutoCreate : function(){
3471          
3472         //if (['right'].indexOf(this.align)!==-1) {
3473         //    cfg.cn[1].cls += ' pull-right'
3474         //}
3475         
3476         
3477         var cfg = {
3478             tag : 'ul',
3479             cls : 'dropdown-menu' ,
3480             style : 'z-index:1000'
3481             
3482         };
3483         
3484         if (this.type === 'submenu') {
3485             cfg.cls = 'submenu active';
3486         }
3487         if (this.type === 'treeview') {
3488             cfg.cls = 'treeview-menu';
3489         }
3490         
3491         return cfg;
3492     },
3493     initEvents : function() {
3494         
3495        // Roo.log("ADD event");
3496        // Roo.log(this.triggerEl.dom);
3497         
3498         this.triggerEl.on('click', this.onTriggerClick, this);
3499         
3500         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3501         
3502         
3503         if (this.triggerEl.hasClass('nav-item')) {
3504             // dropdown toggle on the 'a' in BS4?
3505             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3506         } else {
3507             this.triggerEl.addClass('dropdown-toggle');
3508         }
3509         if (Roo.isTouch) {
3510             this.el.on('touchstart'  , this.onTouch, this);
3511         }
3512         this.el.on('click' , this.onClick, this);
3513
3514         this.el.on("mouseover", this.onMouseOver, this);
3515         this.el.on("mouseout", this.onMouseOut, this);
3516         
3517     },
3518     
3519     findTargetItem : function(e)
3520     {
3521         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3522         if(!t){
3523             return false;
3524         }
3525         //Roo.log(t);         Roo.log(t.id);
3526         if(t && t.id){
3527             //Roo.log(this.menuitems);
3528             return this.menuitems.get(t.id);
3529             
3530             //return this.items.get(t.menuItemId);
3531         }
3532         
3533         return false;
3534     },
3535     
3536     onTouch : function(e) 
3537     {
3538         Roo.log("menu.onTouch");
3539         //e.stopEvent(); this make the user popdown broken
3540         this.onClick(e);
3541     },
3542     
3543     onClick : function(e)
3544     {
3545         Roo.log("menu.onClick");
3546         
3547         var t = this.findTargetItem(e);
3548         if(!t || t.isContainer){
3549             return;
3550         }
3551         Roo.log(e);
3552         /*
3553         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3554             if(t == this.activeItem && t.shouldDeactivate(e)){
3555                 this.activeItem.deactivate();
3556                 delete this.activeItem;
3557                 return;
3558             }
3559             if(t.canActivate){
3560                 this.setActiveItem(t, true);
3561             }
3562             return;
3563             
3564             
3565         }
3566         */
3567        
3568         Roo.log('pass click event');
3569         
3570         t.onClick(e);
3571         
3572         this.fireEvent("click", this, t, e);
3573         
3574         var _this = this;
3575         
3576         if(!t.href.length || t.href == '#'){
3577             (function() { _this.hide(); }).defer(100);
3578         }
3579         
3580     },
3581     
3582     onMouseOver : function(e){
3583         var t  = this.findTargetItem(e);
3584         //Roo.log(t);
3585         //if(t){
3586         //    if(t.canActivate && !t.disabled){
3587         //        this.setActiveItem(t, true);
3588         //    }
3589         //}
3590         
3591         this.fireEvent("mouseover", this, e, t);
3592     },
3593     isVisible : function(){
3594         return !this.hidden;
3595     },
3596     onMouseOut : function(e){
3597         var t  = this.findTargetItem(e);
3598         
3599         //if(t ){
3600         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3601         //        this.activeItem.deactivate();
3602         //        delete this.activeItem;
3603         //    }
3604         //}
3605         this.fireEvent("mouseout", this, e, t);
3606     },
3607     
3608     
3609     /**
3610      * Displays this menu relative to another element
3611      * @param {String/HTMLElement/Roo.Element} element The element to align to
3612      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3613      * the element (defaults to this.defaultAlign)
3614      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3615      */
3616     show : function(el, pos, parentMenu)
3617     {
3618         if (false === this.fireEvent("beforeshow", this)) {
3619             Roo.log("show canceled");
3620             return;
3621         }
3622         this.parentMenu = parentMenu;
3623         if(!this.el){
3624             this.render();
3625         }
3626         
3627         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3628     },
3629      /**
3630      * Displays this menu at a specific xy position
3631      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3632      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3633      */
3634     showAt : function(xy, parentMenu, /* private: */_e){
3635         this.parentMenu = parentMenu;
3636         if(!this.el){
3637             this.render();
3638         }
3639         if(_e !== false){
3640             this.fireEvent("beforeshow", this);
3641             //xy = this.el.adjustForConstraints(xy);
3642         }
3643         
3644         //this.el.show();
3645         this.hideMenuItems();
3646         this.hidden = false;
3647         this.triggerEl.addClass('open');
3648         this.el.addClass('show');
3649         
3650         // reassign x when hitting right
3651         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3652             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3653         }
3654         
3655         // reassign y when hitting bottom
3656         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3657             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3658         }
3659         
3660         // but the list may align on trigger left or trigger top... should it be a properity?
3661         
3662         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3663             this.el.setXY(xy);
3664         }
3665         
3666         this.focus();
3667         this.fireEvent("show", this);
3668     },
3669     
3670     focus : function(){
3671         return;
3672         if(!this.hidden){
3673             this.doFocus.defer(50, this);
3674         }
3675     },
3676
3677     doFocus : function(){
3678         if(!this.hidden){
3679             this.focusEl.focus();
3680         }
3681     },
3682
3683     /**
3684      * Hides this menu and optionally all parent menus
3685      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3686      */
3687     hide : function(deep)
3688     {
3689         if (false === this.fireEvent("beforehide", this)) {
3690             Roo.log("hide canceled");
3691             return;
3692         }
3693         this.hideMenuItems();
3694         if(this.el && this.isVisible()){
3695            
3696             if(this.activeItem){
3697                 this.activeItem.deactivate();
3698                 this.activeItem = null;
3699             }
3700             this.triggerEl.removeClass('open');;
3701             this.el.removeClass('show');
3702             this.hidden = true;
3703             this.fireEvent("hide", this);
3704         }
3705         if(deep === true && this.parentMenu){
3706             this.parentMenu.hide(true);
3707         }
3708     },
3709     
3710     onTriggerClick : function(e)
3711     {
3712         Roo.log('trigger click');
3713         
3714         var target = e.getTarget();
3715         
3716         Roo.log(target.nodeName.toLowerCase());
3717         
3718         if(target.nodeName.toLowerCase() === 'i'){
3719             e.preventDefault();
3720         }
3721         
3722     },
3723     
3724     onTriggerPress  : function(e)
3725     {
3726         Roo.log('trigger press');
3727         //Roo.log(e.getTarget());
3728        // Roo.log(this.triggerEl.dom);
3729        
3730         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3731         var pel = Roo.get(e.getTarget());
3732         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3733             Roo.log('is treeview or dropdown?');
3734             return;
3735         }
3736         
3737         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3738             return;
3739         }
3740         
3741         if (this.isVisible()) {
3742             Roo.log('hide');
3743             this.hide();
3744         } else {
3745             Roo.log('show');
3746             this.show(this.triggerEl, '?', false);
3747         }
3748         
3749         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3750             e.stopEvent();
3751         }
3752         
3753     },
3754        
3755     
3756     hideMenuItems : function()
3757     {
3758         Roo.log("hide Menu Items");
3759         if (!this.el) { 
3760             return;
3761         }
3762         
3763         this.el.select('.open',true).each(function(aa) {
3764             
3765             aa.removeClass('open');
3766          
3767         });
3768     },
3769     addxtypeChild : function (tree, cntr) {
3770         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3771           
3772         this.menuitems.add(comp);
3773         return comp;
3774
3775     },
3776     getEl : function()
3777     {
3778         Roo.log(this.el);
3779         return this.el;
3780     },
3781     
3782     clear : function()
3783     {
3784         this.getEl().dom.innerHTML = '';
3785         this.menuitems.clear();
3786     }
3787 });
3788
3789  
3790  /*
3791  * - LGPL
3792  *
3793  * menu item
3794  * 
3795  */
3796
3797
3798 /**
3799  * @class Roo.bootstrap.MenuItem
3800  * @extends Roo.bootstrap.Component
3801  * Bootstrap MenuItem class
3802  * @cfg {String} html the menu label
3803  * @cfg {String} href the link
3804  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3805  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3806  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3807  * @cfg {String} fa favicon to show on left of menu item.
3808  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3809  * 
3810  * 
3811  * @constructor
3812  * Create a new MenuItem
3813  * @param {Object} config The config object
3814  */
3815
3816
3817 Roo.bootstrap.MenuItem = function(config){
3818     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3819     this.addEvents({
3820         // raw events
3821         /**
3822          * @event click
3823          * The raw click event for the entire grid.
3824          * @param {Roo.bootstrap.MenuItem} this
3825          * @param {Roo.EventObject} e
3826          */
3827         "click" : true
3828     });
3829 };
3830
3831 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3832     
3833     href : false,
3834     html : false,
3835     preventDefault: false,
3836     isContainer : false,
3837     active : false,
3838     fa: false,
3839     
3840     getAutoCreate : function(){
3841         
3842         if(this.isContainer){
3843             return {
3844                 tag: 'li',
3845                 cls: 'dropdown-menu-item '
3846             };
3847         }
3848         var ctag = {
3849             tag: 'span',
3850             html: 'Link'
3851         };
3852         
3853         var anc = {
3854             tag : 'a',
3855             cls : 'dropdown-item',
3856             href : '#',
3857             cn : [  ]
3858         };
3859         
3860         if (this.fa !== false) {
3861             anc.cn.push({
3862                 tag : 'i',
3863                 cls : 'fa fa-' + this.fa
3864             });
3865         }
3866         
3867         anc.cn.push(ctag);
3868         
3869         
3870         var cfg= {
3871             tag: 'li',
3872             cls: 'dropdown-menu-item',
3873             cn: [ anc ]
3874         };
3875         if (this.parent().type == 'treeview') {
3876             cfg.cls = 'treeview-menu';
3877         }
3878         if (this.active) {
3879             cfg.cls += ' active';
3880         }
3881         
3882         
3883         
3884         anc.href = this.href || cfg.cn[0].href ;
3885         ctag.html = this.html || cfg.cn[0].html ;
3886         return cfg;
3887     },
3888     
3889     initEvents: function()
3890     {
3891         if (this.parent().type == 'treeview') {
3892             this.el.select('a').on('click', this.onClick, this);
3893         }
3894         
3895         if (this.menu) {
3896             this.menu.parentType = this.xtype;
3897             this.menu.triggerEl = this.el;
3898             this.menu = this.addxtype(Roo.apply({}, this.menu));
3899         }
3900         
3901     },
3902     onClick : function(e)
3903     {
3904         Roo.log('item on click ');
3905         
3906         if(this.preventDefault){
3907             e.preventDefault();
3908         }
3909         //this.parent().hideMenuItems();
3910         
3911         this.fireEvent('click', this, e);
3912     },
3913     getEl : function()
3914     {
3915         return this.el;
3916     } 
3917 });
3918
3919  
3920
3921  /*
3922  * - LGPL
3923  *
3924  * menu separator
3925  * 
3926  */
3927
3928
3929 /**
3930  * @class Roo.bootstrap.MenuSeparator
3931  * @extends Roo.bootstrap.Component
3932  * Bootstrap MenuSeparator class
3933  * 
3934  * @constructor
3935  * Create a new MenuItem
3936  * @param {Object} config The config object
3937  */
3938
3939
3940 Roo.bootstrap.MenuSeparator = function(config){
3941     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3942 };
3943
3944 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3945     
3946     getAutoCreate : function(){
3947         var cfg = {
3948             cls: 'divider',
3949             tag : 'li'
3950         };
3951         
3952         return cfg;
3953     }
3954    
3955 });
3956
3957  
3958
3959  
3960 /*
3961 * Licence: LGPL
3962 */
3963
3964 /**
3965  * @class Roo.bootstrap.Modal
3966  * @extends Roo.bootstrap.Component
3967  * Bootstrap Modal class
3968  * @cfg {String} title Title of dialog
3969  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3970  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3971  * @cfg {Boolean} specificTitle default false
3972  * @cfg {Array} buttons Array of buttons or standard button set..
3973  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3974  * @cfg {Boolean} animate default true
3975  * @cfg {Boolean} allow_close default true
3976  * @cfg {Boolean} fitwindow default false
3977  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3978  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3979  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3980  * @cfg {String} size (sm|lg|xl) default empty
3981  * @cfg {Number} max_width set the max width of modal
3982  * @cfg {Boolean} editableTitle can the title be edited
3983
3984  *
3985  *
3986  * @constructor
3987  * Create a new Modal Dialog
3988  * @param {Object} config The config object
3989  */
3990
3991 Roo.bootstrap.Modal = function(config){
3992     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3993     this.addEvents({
3994         // raw events
3995         /**
3996          * @event btnclick
3997          * The raw btnclick event for the button
3998          * @param {Roo.EventObject} e
3999          */
4000         "btnclick" : true,
4001         /**
4002          * @event resize
4003          * Fire when dialog resize
4004          * @param {Roo.bootstrap.Modal} this
4005          * @param {Roo.EventObject} e
4006          */
4007         "resize" : true,
4008         /**
4009          * @event titlechanged
4010          * Fire when the editable title has been changed
4011          * @param {Roo.bootstrap.Modal} this
4012          * @param {Roo.EventObject} value
4013          */
4014         "titlechanged" : true 
4015         
4016     });
4017     this.buttons = this.buttons || [];
4018
4019     if (this.tmpl) {
4020         this.tmpl = Roo.factory(this.tmpl);
4021     }
4022
4023 };
4024
4025 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4026
4027     title : 'test dialog',
4028
4029     buttons : false,
4030
4031     // set on load...
4032
4033     html: false,
4034
4035     tmp: false,
4036
4037     specificTitle: false,
4038
4039     buttonPosition: 'right',
4040
4041     allow_close : true,
4042
4043     animate : true,
4044
4045     fitwindow: false,
4046     
4047      // private
4048     dialogEl: false,
4049     bodyEl:  false,
4050     footerEl:  false,
4051     titleEl:  false,
4052     closeEl:  false,
4053
4054     size: '',
4055     
4056     max_width: 0,
4057     
4058     max_height: 0,
4059     
4060     fit_content: false,
4061     editableTitle  : false,
4062
4063     onRender : function(ct, position)
4064     {
4065         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4066
4067         if(!this.el){
4068             var cfg = Roo.apply({},  this.getAutoCreate());
4069             cfg.id = Roo.id();
4070             //if(!cfg.name){
4071             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4072             //}
4073             //if (!cfg.name.length) {
4074             //    delete cfg.name;
4075            // }
4076             if (this.cls) {
4077                 cfg.cls += ' ' + this.cls;
4078             }
4079             if (this.style) {
4080                 cfg.style = this.style;
4081             }
4082             this.el = Roo.get(document.body).createChild(cfg, position);
4083         }
4084         //var type = this.el.dom.type;
4085
4086
4087         if(this.tabIndex !== undefined){
4088             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4089         }
4090
4091         this.dialogEl = this.el.select('.modal-dialog',true).first();
4092         this.bodyEl = this.el.select('.modal-body',true).first();
4093         this.closeEl = this.el.select('.modal-header .close', true).first();
4094         this.headerEl = this.el.select('.modal-header',true).first();
4095         this.titleEl = this.el.select('.modal-title',true).first();
4096         this.footerEl = this.el.select('.modal-footer',true).first();
4097
4098         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4099         
4100         //this.el.addClass("x-dlg-modal");
4101
4102         if (this.buttons.length) {
4103             Roo.each(this.buttons, function(bb) {
4104                 var b = Roo.apply({}, bb);
4105                 b.xns = b.xns || Roo.bootstrap;
4106                 b.xtype = b.xtype || 'Button';
4107                 if (typeof(b.listeners) == 'undefined') {
4108                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4109                 }
4110
4111                 var btn = Roo.factory(b);
4112
4113                 btn.render(this.getButtonContainer());
4114
4115             },this);
4116         }
4117         // render the children.
4118         var nitems = [];
4119
4120         if(typeof(this.items) != 'undefined'){
4121             var items = this.items;
4122             delete this.items;
4123
4124             for(var i =0;i < items.length;i++) {
4125                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4126             }
4127         }
4128
4129         this.items = nitems;
4130
4131         // where are these used - they used to be body/close/footer
4132
4133
4134         this.initEvents();
4135         //this.el.addClass([this.fieldClass, this.cls]);
4136
4137     },
4138
4139     getAutoCreate : function()
4140     {
4141         // we will default to modal-body-overflow - might need to remove or make optional later.
4142         var bdy = {
4143                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4144                 html : this.html || ''
4145         };
4146
4147         var title = {
4148             tag: 'h5',
4149             cls : 'modal-title',
4150             html : this.title
4151         };
4152
4153         if(this.specificTitle){ // WTF is this?
4154             title = this.title;
4155         }
4156
4157         var header = [];
4158         if (this.allow_close && Roo.bootstrap.version == 3) {
4159             header.push({
4160                 tag: 'button',
4161                 cls : 'close',
4162                 html : '&times'
4163             });
4164         }
4165
4166         header.push(title);
4167
4168         if (this.editableTitle) {
4169             header.push({
4170                 cls: 'form-control roo-editable-title d-none',
4171                 tag: 'input',
4172                 type: 'text'
4173             });
4174         }
4175         
4176         if (this.allow_close && Roo.bootstrap.version == 4) {
4177             header.push({
4178                 tag: 'button',
4179                 cls : 'close',
4180                 html : '&times'
4181             });
4182         }
4183         
4184         var size = '';
4185
4186         if(this.size.length){
4187             size = 'modal-' + this.size;
4188         }
4189         
4190         var footer = Roo.bootstrap.version == 3 ?
4191             {
4192                 cls : 'modal-footer',
4193                 cn : [
4194                     {
4195                         tag: 'div',
4196                         cls: 'btn-' + this.buttonPosition
4197                     }
4198                 ]
4199
4200             } :
4201             {  // BS4 uses mr-auto on left buttons....
4202                 cls : 'modal-footer'
4203             };
4204
4205             
4206
4207         
4208         
4209         var modal = {
4210             cls: "modal",
4211              cn : [
4212                 {
4213                     cls: "modal-dialog " + size,
4214                     cn : [
4215                         {
4216                             cls : "modal-content",
4217                             cn : [
4218                                 {
4219                                     cls : 'modal-header',
4220                                     cn : header
4221                                 },
4222                                 bdy,
4223                                 footer
4224                             ]
4225
4226                         }
4227                     ]
4228
4229                 }
4230             ]
4231         };
4232
4233         if(this.animate){
4234             modal.cls += ' fade';
4235         }
4236
4237         return modal;
4238
4239     },
4240     getChildContainer : function() {
4241
4242          return this.bodyEl;
4243
4244     },
4245     getButtonContainer : function() {
4246         
4247          return Roo.bootstrap.version == 4 ?
4248             this.el.select('.modal-footer',true).first()
4249             : this.el.select('.modal-footer div',true).first();
4250
4251     },
4252     initEvents : function()
4253     {
4254         if (this.allow_close) {
4255             this.closeEl.on('click', this.hide, this);
4256         }
4257         Roo.EventManager.onWindowResize(this.resize, this, true);
4258         if (this.editableTitle) {
4259             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4260             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4261             this.headerEditEl.on('keyup', function(e) {
4262                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4263                         this.toggleHeaderInput(false)
4264                     }
4265                 }, this);
4266             this.headerEditEl.on('blur', function(e) {
4267                 this.toggleHeaderInput(false)
4268             },this);
4269         }
4270
4271     },
4272   
4273
4274     resize : function()
4275     {
4276         this.maskEl.setSize(
4277             Roo.lib.Dom.getViewWidth(true),
4278             Roo.lib.Dom.getViewHeight(true)
4279         );
4280         
4281         if (this.fitwindow) {
4282             
4283            this.dialogEl.setStyle( { 'max-width' : '100%' });
4284             this.setSize(
4285                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4286                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4287             );
4288             return;
4289         }
4290         
4291         if(this.max_width !== 0) {
4292             
4293             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4294             
4295             if(this.height) {
4296                 this.setSize(w, this.height);
4297                 return;
4298             }
4299             
4300             if(this.max_height) {
4301                 this.setSize(w,Math.min(
4302                     this.max_height,
4303                     Roo.lib.Dom.getViewportHeight(true) - 60
4304                 ));
4305                 
4306                 return;
4307             }
4308             
4309             if(!this.fit_content) {
4310                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4311                 return;
4312             }
4313             
4314             this.setSize(w, Math.min(
4315                 60 +
4316                 this.headerEl.getHeight() + 
4317                 this.footerEl.getHeight() + 
4318                 this.getChildHeight(this.bodyEl.dom.childNodes),
4319                 Roo.lib.Dom.getViewportHeight(true) - 60)
4320             );
4321         }
4322         
4323     },
4324
4325     setSize : function(w,h)
4326     {
4327         if (!w && !h) {
4328             return;
4329         }
4330         
4331         this.resizeTo(w,h);
4332     },
4333
4334     show : function() {
4335
4336         if (!this.rendered) {
4337             this.render();
4338         }
4339         this.toggleHeaderInput(false);
4340         //this.el.setStyle('display', 'block');
4341         this.el.removeClass('hideing');
4342         this.el.dom.style.display='block';
4343         
4344         Roo.get(document.body).addClass('modal-open');
4345  
4346         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4347             
4348             (function(){
4349                 this.el.addClass('show');
4350                 this.el.addClass('in');
4351             }).defer(50, this);
4352         }else{
4353             this.el.addClass('show');
4354             this.el.addClass('in');
4355         }
4356
4357         // not sure how we can show data in here..
4358         //if (this.tmpl) {
4359         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4360         //}
4361
4362         Roo.get(document.body).addClass("x-body-masked");
4363         
4364         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4365         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4366         this.maskEl.dom.style.display = 'block';
4367         this.maskEl.addClass('show');
4368         
4369         
4370         this.resize();
4371         
4372         this.fireEvent('show', this);
4373
4374         // set zindex here - otherwise it appears to be ignored...
4375         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4376
4377         (function () {
4378             this.items.forEach( function(e) {
4379                 e.layout ? e.layout() : false;
4380
4381             });
4382         }).defer(100,this);
4383
4384     },
4385     hide : function()
4386     {
4387         if(this.fireEvent("beforehide", this) !== false){
4388             
4389             this.maskEl.removeClass('show');
4390             
4391             this.maskEl.dom.style.display = '';
4392             Roo.get(document.body).removeClass("x-body-masked");
4393             this.el.removeClass('in');
4394             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4395
4396             if(this.animate){ // why
4397                 this.el.addClass('hideing');
4398                 this.el.removeClass('show');
4399                 (function(){
4400                     if (!this.el.hasClass('hideing')) {
4401                         return; // it's been shown again...
4402                     }
4403                     
4404                     this.el.dom.style.display='';
4405
4406                     Roo.get(document.body).removeClass('modal-open');
4407                     this.el.removeClass('hideing');
4408                 }).defer(150,this);
4409                 
4410             }else{
4411                 this.el.removeClass('show');
4412                 this.el.dom.style.display='';
4413                 Roo.get(document.body).removeClass('modal-open');
4414
4415             }
4416             this.fireEvent('hide', this);
4417         }
4418     },
4419     isVisible : function()
4420     {
4421         
4422         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4423         
4424     },
4425
4426     addButton : function(str, cb)
4427     {
4428
4429
4430         var b = Roo.apply({}, { html : str } );
4431         b.xns = b.xns || Roo.bootstrap;
4432         b.xtype = b.xtype || 'Button';
4433         if (typeof(b.listeners) == 'undefined') {
4434             b.listeners = { click : cb.createDelegate(this)  };
4435         }
4436
4437         var btn = Roo.factory(b);
4438
4439         btn.render(this.getButtonContainer());
4440
4441         return btn;
4442
4443     },
4444
4445     setDefaultButton : function(btn)
4446     {
4447         //this.el.select('.modal-footer').()
4448     },
4449
4450     resizeTo: function(w,h)
4451     {
4452         this.dialogEl.setWidth(w);
4453         
4454         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4455
4456         this.bodyEl.setHeight(h - diff);
4457         
4458         this.fireEvent('resize', this);
4459     },
4460     
4461     setContentSize  : function(w, h)
4462     {
4463
4464     },
4465     onButtonClick: function(btn,e)
4466     {
4467         //Roo.log([a,b,c]);
4468         this.fireEvent('btnclick', btn.name, e);
4469     },
4470      /**
4471      * Set the title of the Dialog
4472      * @param {String} str new Title
4473      */
4474     setTitle: function(str) {
4475         this.titleEl.dom.innerHTML = str;
4476         this.title = str;
4477     },
4478     /**
4479      * Set the body of the Dialog
4480      * @param {String} str new Title
4481      */
4482     setBody: function(str) {
4483         this.bodyEl.dom.innerHTML = str;
4484     },
4485     /**
4486      * Set the body of the Dialog using the template
4487      * @param {Obj} data - apply this data to the template and replace the body contents.
4488      */
4489     applyBody: function(obj)
4490     {
4491         if (!this.tmpl) {
4492             Roo.log("Error - using apply Body without a template");
4493             //code
4494         }
4495         this.tmpl.overwrite(this.bodyEl, obj);
4496     },
4497     
4498     getChildHeight : function(child_nodes)
4499     {
4500         if(
4501             !child_nodes ||
4502             child_nodes.length == 0
4503         ) {
4504             return 0;
4505         }
4506         
4507         var child_height = 0;
4508         
4509         for(var i = 0; i < child_nodes.length; i++) {
4510             
4511             /*
4512             * for modal with tabs...
4513             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4514                 
4515                 var layout_childs = child_nodes[i].childNodes;
4516                 
4517                 for(var j = 0; j < layout_childs.length; j++) {
4518                     
4519                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4520                         
4521                         var layout_body_childs = layout_childs[j].childNodes;
4522                         
4523                         for(var k = 0; k < layout_body_childs.length; k++) {
4524                             
4525                             if(layout_body_childs[k].classList.contains('navbar')) {
4526                                 child_height += layout_body_childs[k].offsetHeight;
4527                                 continue;
4528                             }
4529                             
4530                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4531                                 
4532                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4533                                 
4534                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4535                                     
4536                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4537                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4538                                         continue;
4539                                     }
4540                                     
4541                                 }
4542                                 
4543                             }
4544                             
4545                         }
4546                     }
4547                 }
4548                 continue;
4549             }
4550             */
4551             
4552             child_height += child_nodes[i].offsetHeight;
4553             // Roo.log(child_nodes[i].offsetHeight);
4554         }
4555         
4556         return child_height;
4557     },
4558     toggleHeaderInput : function(is_edit)
4559     {
4560         if (!this.editableTitle) {
4561             return; // not editable.
4562         }
4563         if (is_edit && this.is_header_editing) {
4564             return; // already editing..
4565         }
4566         if (is_edit) {
4567     
4568             this.headerEditEl.dom.value = this.title;
4569             this.headerEditEl.removeClass('d-none');
4570             this.headerEditEl.dom.focus();
4571             this.titleEl.addClass('d-none');
4572             
4573             this.is_header_editing = true;
4574             return
4575         }
4576         // flip back to not editing.
4577         this.title = this.headerEditEl.dom.value;
4578         this.headerEditEl.addClass('d-none');
4579         this.titleEl.removeClass('d-none');
4580         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4581         this.is_header_editing = false;
4582         this.fireEvent('titlechanged', this, this.title);
4583     
4584             
4585         
4586     }
4587
4588 });
4589
4590
4591 Roo.apply(Roo.bootstrap.Modal,  {
4592     /**
4593          * Button config that displays a single OK button
4594          * @type Object
4595          */
4596         OK :  [{
4597             name : 'ok',
4598             weight : 'primary',
4599             html : 'OK'
4600         }],
4601         /**
4602          * Button config that displays Yes and No buttons
4603          * @type Object
4604          */
4605         YESNO : [
4606             {
4607                 name  : 'no',
4608                 html : 'No'
4609             },
4610             {
4611                 name  :'yes',
4612                 weight : 'primary',
4613                 html : 'Yes'
4614             }
4615         ],
4616
4617         /**
4618          * Button config that displays OK and Cancel buttons
4619          * @type Object
4620          */
4621         OKCANCEL : [
4622             {
4623                name : 'cancel',
4624                 html : 'Cancel'
4625             },
4626             {
4627                 name : 'ok',
4628                 weight : 'primary',
4629                 html : 'OK'
4630             }
4631         ],
4632         /**
4633          * Button config that displays Yes, No and Cancel buttons
4634          * @type Object
4635          */
4636         YESNOCANCEL : [
4637             {
4638                 name : 'yes',
4639                 weight : 'primary',
4640                 html : 'Yes'
4641             },
4642             {
4643                 name : 'no',
4644                 html : 'No'
4645             },
4646             {
4647                 name : 'cancel',
4648                 html : 'Cancel'
4649             }
4650         ],
4651         
4652         zIndex : 10001
4653 });
4654
4655 /*
4656  * - LGPL
4657  *
4658  * messagebox - can be used as a replace
4659  * 
4660  */
4661 /**
4662  * @class Roo.MessageBox
4663  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4664  * Example usage:
4665  *<pre><code>
4666 // Basic alert:
4667 Roo.Msg.alert('Status', 'Changes saved successfully.');
4668
4669 // Prompt for user data:
4670 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4671     if (btn == 'ok'){
4672         // process text value...
4673     }
4674 });
4675
4676 // Show a dialog using config options:
4677 Roo.Msg.show({
4678    title:'Save Changes?',
4679    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4680    buttons: Roo.Msg.YESNOCANCEL,
4681    fn: processResult,
4682    animEl: 'elId'
4683 });
4684 </code></pre>
4685  * @singleton
4686  */
4687 Roo.bootstrap.MessageBox = function(){
4688     var dlg, opt, mask, waitTimer;
4689     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4690     var buttons, activeTextEl, bwidth;
4691
4692     
4693     // private
4694     var handleButton = function(button){
4695         dlg.hide();
4696         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4697     };
4698
4699     // private
4700     var handleHide = function(){
4701         if(opt && opt.cls){
4702             dlg.el.removeClass(opt.cls);
4703         }
4704         //if(waitTimer){
4705         //    Roo.TaskMgr.stop(waitTimer);
4706         //    waitTimer = null;
4707         //}
4708     };
4709
4710     // private
4711     var updateButtons = function(b){
4712         var width = 0;
4713         if(!b){
4714             buttons["ok"].hide();
4715             buttons["cancel"].hide();
4716             buttons["yes"].hide();
4717             buttons["no"].hide();
4718             dlg.footerEl.hide();
4719             
4720             return width;
4721         }
4722         dlg.footerEl.show();
4723         for(var k in buttons){
4724             if(typeof buttons[k] != "function"){
4725                 if(b[k]){
4726                     buttons[k].show();
4727                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4728                     width += buttons[k].el.getWidth()+15;
4729                 }else{
4730                     buttons[k].hide();
4731                 }
4732             }
4733         }
4734         return width;
4735     };
4736
4737     // private
4738     var handleEsc = function(d, k, e){
4739         if(opt && opt.closable !== false){
4740             dlg.hide();
4741         }
4742         if(e){
4743             e.stopEvent();
4744         }
4745     };
4746
4747     return {
4748         /**
4749          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4750          * @return {Roo.BasicDialog} The BasicDialog element
4751          */
4752         getDialog : function(){
4753            if(!dlg){
4754                 dlg = new Roo.bootstrap.Modal( {
4755                     //draggable: true,
4756                     //resizable:false,
4757                     //constraintoviewport:false,
4758                     //fixedcenter:true,
4759                     //collapsible : false,
4760                     //shim:true,
4761                     //modal: true,
4762                 //    width: 'auto',
4763                   //  height:100,
4764                     //buttonAlign:"center",
4765                     closeClick : function(){
4766                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4767                             handleButton("no");
4768                         }else{
4769                             handleButton("cancel");
4770                         }
4771                     }
4772                 });
4773                 dlg.render();
4774                 dlg.on("hide", handleHide);
4775                 mask = dlg.mask;
4776                 //dlg.addKeyListener(27, handleEsc);
4777                 buttons = {};
4778                 this.buttons = buttons;
4779                 var bt = this.buttonText;
4780                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4781                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4782                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4783                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4784                 //Roo.log(buttons);
4785                 bodyEl = dlg.bodyEl.createChild({
4786
4787                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4788                         '<textarea class="roo-mb-textarea"></textarea>' +
4789                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4790                 });
4791                 msgEl = bodyEl.dom.firstChild;
4792                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4793                 textboxEl.enableDisplayMode();
4794                 textboxEl.addKeyListener([10,13], function(){
4795                     if(dlg.isVisible() && opt && opt.buttons){
4796                         if(opt.buttons.ok){
4797                             handleButton("ok");
4798                         }else if(opt.buttons.yes){
4799                             handleButton("yes");
4800                         }
4801                     }
4802                 });
4803                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4804                 textareaEl.enableDisplayMode();
4805                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4806                 progressEl.enableDisplayMode();
4807                 
4808                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4809                 var pf = progressEl.dom.firstChild;
4810                 if (pf) {
4811                     pp = Roo.get(pf.firstChild);
4812                     pp.setHeight(pf.offsetHeight);
4813                 }
4814                 
4815             }
4816             return dlg;
4817         },
4818
4819         /**
4820          * Updates the message box body text
4821          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4822          * the XHTML-compliant non-breaking space character '&amp;#160;')
4823          * @return {Roo.MessageBox} This message box
4824          */
4825         updateText : function(text)
4826         {
4827             if(!dlg.isVisible() && !opt.width){
4828                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4829                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4830             }
4831             msgEl.innerHTML = text || '&#160;';
4832       
4833             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4834             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4835             var w = Math.max(
4836                     Math.min(opt.width || cw , this.maxWidth), 
4837                     Math.max(opt.minWidth || this.minWidth, bwidth)
4838             );
4839             if(opt.prompt){
4840                 activeTextEl.setWidth(w);
4841             }
4842             if(dlg.isVisible()){
4843                 dlg.fixedcenter = false;
4844             }
4845             // to big, make it scroll. = But as usual stupid IE does not support
4846             // !important..
4847             
4848             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4849                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4850                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4851             } else {
4852                 bodyEl.dom.style.height = '';
4853                 bodyEl.dom.style.overflowY = '';
4854             }
4855             if (cw > w) {
4856                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4857             } else {
4858                 bodyEl.dom.style.overflowX = '';
4859             }
4860             
4861             dlg.setContentSize(w, bodyEl.getHeight());
4862             if(dlg.isVisible()){
4863                 dlg.fixedcenter = true;
4864             }
4865             return this;
4866         },
4867
4868         /**
4869          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4870          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4871          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4872          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4873          * @return {Roo.MessageBox} This message box
4874          */
4875         updateProgress : function(value, text){
4876             if(text){
4877                 this.updateText(text);
4878             }
4879             
4880             if (pp) { // weird bug on my firefox - for some reason this is not defined
4881                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4882                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4883             }
4884             return this;
4885         },        
4886
4887         /**
4888          * Returns true if the message box is currently displayed
4889          * @return {Boolean} True if the message box is visible, else false
4890          */
4891         isVisible : function(){
4892             return dlg && dlg.isVisible();  
4893         },
4894
4895         /**
4896          * Hides the message box if it is displayed
4897          */
4898         hide : function(){
4899             if(this.isVisible()){
4900                 dlg.hide();
4901             }  
4902         },
4903
4904         /**
4905          * Displays a new message box, or reinitializes an existing message box, based on the config options
4906          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4907          * The following config object properties are supported:
4908          * <pre>
4909 Property    Type             Description
4910 ----------  ---------------  ------------------------------------------------------------------------------------
4911 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4912                                    closes (defaults to undefined)
4913 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4914                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4915 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4916                                    progress and wait dialogs will ignore this property and always hide the
4917                                    close button as they can only be closed programmatically.
4918 cls               String           A custom CSS class to apply to the message box element
4919 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4920                                    displayed (defaults to 75)
4921 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4922                                    function will be btn (the name of the button that was clicked, if applicable,
4923                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4924                                    Progress and wait dialogs will ignore this option since they do not respond to
4925                                    user actions and can only be closed programmatically, so any required function
4926                                    should be called by the same code after it closes the dialog.
4927 icon              String           A CSS class that provides a background image to be used as an icon for
4928                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4929 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4930 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4931 modal             Boolean          False to allow user interaction with the page while the message box is
4932                                    displayed (defaults to true)
4933 msg               String           A string that will replace the existing message box body text (defaults
4934                                    to the XHTML-compliant non-breaking space character '&#160;')
4935 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4936 progress          Boolean          True to display a progress bar (defaults to false)
4937 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4938 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4939 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4940 title             String           The title text
4941 value             String           The string value to set into the active textbox element if displayed
4942 wait              Boolean          True to display a progress bar (defaults to false)
4943 width             Number           The width of the dialog in pixels
4944 </pre>
4945          *
4946          * Example usage:
4947          * <pre><code>
4948 Roo.Msg.show({
4949    title: 'Address',
4950    msg: 'Please enter your address:',
4951    width: 300,
4952    buttons: Roo.MessageBox.OKCANCEL,
4953    multiline: true,
4954    fn: saveAddress,
4955    animEl: 'addAddressBtn'
4956 });
4957 </code></pre>
4958          * @param {Object} config Configuration options
4959          * @return {Roo.MessageBox} This message box
4960          */
4961         show : function(options)
4962         {
4963             
4964             // this causes nightmares if you show one dialog after another
4965             // especially on callbacks..
4966              
4967             if(this.isVisible()){
4968                 
4969                 this.hide();
4970                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4971                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4972                 Roo.log("New Dialog Message:" +  options.msg )
4973                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4974                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4975                 
4976             }
4977             var d = this.getDialog();
4978             opt = options;
4979             d.setTitle(opt.title || "&#160;");
4980             d.closeEl.setDisplayed(opt.closable !== false);
4981             activeTextEl = textboxEl;
4982             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4983             if(opt.prompt){
4984                 if(opt.multiline){
4985                     textboxEl.hide();
4986                     textareaEl.show();
4987                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4988                         opt.multiline : this.defaultTextHeight);
4989                     activeTextEl = textareaEl;
4990                 }else{
4991                     textboxEl.show();
4992                     textareaEl.hide();
4993                 }
4994             }else{
4995                 textboxEl.hide();
4996                 textareaEl.hide();
4997             }
4998             progressEl.setDisplayed(opt.progress === true);
4999             if (opt.progress) {
5000                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
5001             }
5002             this.updateProgress(0);
5003             activeTextEl.dom.value = opt.value || "";
5004             if(opt.prompt){
5005                 dlg.setDefaultButton(activeTextEl);
5006             }else{
5007                 var bs = opt.buttons;
5008                 var db = null;
5009                 if(bs && bs.ok){
5010                     db = buttons["ok"];
5011                 }else if(bs && bs.yes){
5012                     db = buttons["yes"];
5013                 }
5014                 dlg.setDefaultButton(db);
5015             }
5016             bwidth = updateButtons(opt.buttons);
5017             this.updateText(opt.msg);
5018             if(opt.cls){
5019                 d.el.addClass(opt.cls);
5020             }
5021             d.proxyDrag = opt.proxyDrag === true;
5022             d.modal = opt.modal !== false;
5023             d.mask = opt.modal !== false ? mask : false;
5024             if(!d.isVisible()){
5025                 // force it to the end of the z-index stack so it gets a cursor in FF
5026                 document.body.appendChild(dlg.el.dom);
5027                 d.animateTarget = null;
5028                 d.show(options.animEl);
5029             }
5030             return this;
5031         },
5032
5033         /**
5034          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5035          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5036          * and closing the message box when the process is complete.
5037          * @param {String} title The title bar text
5038          * @param {String} msg The message box body text
5039          * @return {Roo.MessageBox} This message box
5040          */
5041         progress : function(title, msg){
5042             this.show({
5043                 title : title,
5044                 msg : msg,
5045                 buttons: false,
5046                 progress:true,
5047                 closable:false,
5048                 minWidth: this.minProgressWidth,
5049                 modal : true
5050             });
5051             return this;
5052         },
5053
5054         /**
5055          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5056          * If a callback function is passed it will be called after the user clicks the button, and the
5057          * id of the button that was clicked will be passed as the only parameter to the callback
5058          * (could also be the top-right close button).
5059          * @param {String} title The title bar text
5060          * @param {String} msg The message box body text
5061          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5062          * @param {Object} scope (optional) The scope of the callback function
5063          * @return {Roo.MessageBox} This message box
5064          */
5065         alert : function(title, msg, fn, scope)
5066         {
5067             this.show({
5068                 title : title,
5069                 msg : msg,
5070                 buttons: this.OK,
5071                 fn: fn,
5072                 closable : false,
5073                 scope : scope,
5074                 modal : true
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5081          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5082          * You are responsible for closing the message box when the process is complete.
5083          * @param {String} msg The message box body text
5084          * @param {String} title (optional) The title bar text
5085          * @return {Roo.MessageBox} This message box
5086          */
5087         wait : function(msg, title){
5088             this.show({
5089                 title : title,
5090                 msg : msg,
5091                 buttons: false,
5092                 closable:false,
5093                 progress:true,
5094                 modal:true,
5095                 width:300,
5096                 wait:true
5097             });
5098             waitTimer = Roo.TaskMgr.start({
5099                 run: function(i){
5100                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5101                 },
5102                 interval: 1000
5103             });
5104             return this;
5105         },
5106
5107         /**
5108          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5109          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5110          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5111          * @param {String} title The title bar text
5112          * @param {String} msg The message box body text
5113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114          * @param {Object} scope (optional) The scope of the callback function
5115          * @return {Roo.MessageBox} This message box
5116          */
5117         confirm : function(title, msg, fn, scope){
5118             this.show({
5119                 title : title,
5120                 msg : msg,
5121                 buttons: this.YESNO,
5122                 fn: fn,
5123                 scope : scope,
5124                 modal : true
5125             });
5126             return this;
5127         },
5128
5129         /**
5130          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5131          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5132          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5133          * (could also be the top-right close button) and the text that was entered will be passed as the two
5134          * parameters to the callback.
5135          * @param {String} title The title bar text
5136          * @param {String} msg The message box body text
5137          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5138          * @param {Object} scope (optional) The scope of the callback function
5139          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5140          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5141          * @return {Roo.MessageBox} This message box
5142          */
5143         prompt : function(title, msg, fn, scope, multiline){
5144             this.show({
5145                 title : title,
5146                 msg : msg,
5147                 buttons: this.OKCANCEL,
5148                 fn: fn,
5149                 minWidth:250,
5150                 scope : scope,
5151                 prompt:true,
5152                 multiline: multiline,
5153                 modal : true
5154             });
5155             return this;
5156         },
5157
5158         /**
5159          * Button config that displays a single OK button
5160          * @type Object
5161          */
5162         OK : {ok:true},
5163         /**
5164          * Button config that displays Yes and No buttons
5165          * @type Object
5166          */
5167         YESNO : {yes:true, no:true},
5168         /**
5169          * Button config that displays OK and Cancel buttons
5170          * @type Object
5171          */
5172         OKCANCEL : {ok:true, cancel:true},
5173         /**
5174          * Button config that displays Yes, No and Cancel buttons
5175          * @type Object
5176          */
5177         YESNOCANCEL : {yes:true, no:true, cancel:true},
5178
5179         /**
5180          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5181          * @type Number
5182          */
5183         defaultTextHeight : 75,
5184         /**
5185          * The maximum width in pixels of the message box (defaults to 600)
5186          * @type Number
5187          */
5188         maxWidth : 600,
5189         /**
5190          * The minimum width in pixels of the message box (defaults to 100)
5191          * @type Number
5192          */
5193         minWidth : 100,
5194         /**
5195          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5196          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5197          * @type Number
5198          */
5199         minProgressWidth : 250,
5200         /**
5201          * An object containing the default button text strings that can be overriden for localized language support.
5202          * Supported properties are: ok, cancel, yes and no.
5203          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5204          * @type Object
5205          */
5206         buttonText : {
5207             ok : "OK",
5208             cancel : "Cancel",
5209             yes : "Yes",
5210             no : "No"
5211         }
5212     };
5213 }();
5214
5215 /**
5216  * Shorthand for {@link Roo.MessageBox}
5217  */
5218 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5219 Roo.Msg = Roo.Msg || Roo.MessageBox;
5220 /*
5221  * - LGPL
5222  *
5223  * navbar
5224  * 
5225  */
5226
5227 /**
5228  * @class Roo.bootstrap.Navbar
5229  * @extends Roo.bootstrap.Component
5230  * Bootstrap Navbar class
5231
5232  * @constructor
5233  * Create a new Navbar
5234  * @param {Object} config The config object
5235  */
5236
5237
5238 Roo.bootstrap.Navbar = function(config){
5239     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5240     this.addEvents({
5241         // raw events
5242         /**
5243          * @event beforetoggle
5244          * Fire before toggle the menu
5245          * @param {Roo.EventObject} e
5246          */
5247         "beforetoggle" : true
5248     });
5249 };
5250
5251 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5252     
5253     
5254    
5255     // private
5256     navItems : false,
5257     loadMask : false,
5258     
5259     
5260     getAutoCreate : function(){
5261         
5262         
5263         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5264         
5265     },
5266     
5267     initEvents :function ()
5268     {
5269         //Roo.log(this.el.select('.navbar-toggle',true));
5270         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5271         
5272         var mark = {
5273             tag: "div",
5274             cls:"x-dlg-mask"
5275         };
5276         
5277         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5278         
5279         var size = this.el.getSize();
5280         this.maskEl.setSize(size.width, size.height);
5281         this.maskEl.enableDisplayMode("block");
5282         this.maskEl.hide();
5283         
5284         if(this.loadMask){
5285             this.maskEl.show();
5286         }
5287     },
5288     
5289     
5290     getChildContainer : function()
5291     {
5292         if (this.el && this.el.select('.collapse').getCount()) {
5293             return this.el.select('.collapse',true).first();
5294         }
5295         
5296         return this.el;
5297     },
5298     
5299     mask : function()
5300     {
5301         this.maskEl.show();
5302     },
5303     
5304     unmask : function()
5305     {
5306         this.maskEl.hide();
5307     },
5308     onToggle : function()
5309     {
5310         
5311         if(this.fireEvent('beforetoggle', this) === false){
5312             return;
5313         }
5314         var ce = this.el.select('.navbar-collapse',true).first();
5315       
5316         if (!ce.hasClass('show')) {
5317            this.expand();
5318         } else {
5319             this.collapse();
5320         }
5321         
5322         
5323     
5324     },
5325     /**
5326      * Expand the navbar pulldown 
5327      */
5328     expand : function ()
5329     {
5330        
5331         var ce = this.el.select('.navbar-collapse',true).first();
5332         if (ce.hasClass('collapsing')) {
5333             return;
5334         }
5335         ce.dom.style.height = '';
5336                // show it...
5337         ce.addClass('in'); // old...
5338         ce.removeClass('collapse');
5339         ce.addClass('show');
5340         var h = ce.getHeight();
5341         Roo.log(h);
5342         ce.removeClass('show');
5343         // at this point we should be able to see it..
5344         ce.addClass('collapsing');
5345         
5346         ce.setHeight(0); // resize it ...
5347         ce.on('transitionend', function() {
5348             //Roo.log('done transition');
5349             ce.removeClass('collapsing');
5350             ce.addClass('show');
5351             ce.removeClass('collapse');
5352
5353             ce.dom.style.height = '';
5354         }, this, { single: true} );
5355         ce.setHeight(h);
5356         ce.dom.scrollTop = 0;
5357     },
5358     /**
5359      * Collapse the navbar pulldown 
5360      */
5361     collapse : function()
5362     {
5363          var ce = this.el.select('.navbar-collapse',true).first();
5364        
5365         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5366             // it's collapsed or collapsing..
5367             return;
5368         }
5369         ce.removeClass('in'); // old...
5370         ce.setHeight(ce.getHeight());
5371         ce.removeClass('show');
5372         ce.addClass('collapsing');
5373         
5374         ce.on('transitionend', function() {
5375             ce.dom.style.height = '';
5376             ce.removeClass('collapsing');
5377             ce.addClass('collapse');
5378         }, this, { single: true} );
5379         ce.setHeight(0);
5380     }
5381     
5382     
5383     
5384 });
5385
5386
5387
5388  
5389
5390  /*
5391  * - LGPL
5392  *
5393  * navbar
5394  * 
5395  */
5396
5397 /**
5398  * @class Roo.bootstrap.NavSimplebar
5399  * @extends Roo.bootstrap.Navbar
5400  * Bootstrap Sidebar class
5401  *
5402  * @cfg {Boolean} inverse is inverted color
5403  * 
5404  * @cfg {String} type (nav | pills | tabs)
5405  * @cfg {Boolean} arrangement stacked | justified
5406  * @cfg {String} align (left | right) alignment
5407  * 
5408  * @cfg {Boolean} main (true|false) main nav bar? default false
5409  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5410  * 
5411  * @cfg {String} tag (header|footer|nav|div) default is nav 
5412
5413  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5414  * 
5415  * 
5416  * @constructor
5417  * Create a new Sidebar
5418  * @param {Object} config The config object
5419  */
5420
5421
5422 Roo.bootstrap.NavSimplebar = function(config){
5423     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5424 };
5425
5426 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5427     
5428     inverse: false,
5429     
5430     type: false,
5431     arrangement: '',
5432     align : false,
5433     
5434     weight : 'light',
5435     
5436     main : false,
5437     
5438     
5439     tag : false,
5440     
5441     
5442     getAutoCreate : function(){
5443         
5444         
5445         var cfg = {
5446             tag : this.tag || 'div',
5447             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5448         };
5449         if (['light','white'].indexOf(this.weight) > -1) {
5450             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5451         }
5452         cfg.cls += ' bg-' + this.weight;
5453         
5454         if (this.inverse) {
5455             cfg.cls += ' navbar-inverse';
5456             
5457         }
5458         
5459         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5460         
5461         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5462             return cfg;
5463         }
5464         
5465         
5466     
5467         
5468         cfg.cn = [
5469             {
5470                 cls: 'nav nav-' + this.xtype,
5471                 tag : 'ul'
5472             }
5473         ];
5474         
5475          
5476         this.type = this.type || 'nav';
5477         if (['tabs','pills'].indexOf(this.type) != -1) {
5478             cfg.cn[0].cls += ' nav-' + this.type
5479         
5480         
5481         } else {
5482             if (this.type!=='nav') {
5483                 Roo.log('nav type must be nav/tabs/pills')
5484             }
5485             cfg.cn[0].cls += ' navbar-nav'
5486         }
5487         
5488         
5489         
5490         
5491         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5492             cfg.cn[0].cls += ' nav-' + this.arrangement;
5493         }
5494         
5495         
5496         if (this.align === 'right') {
5497             cfg.cn[0].cls += ' navbar-right';
5498         }
5499         
5500         
5501         
5502         
5503         return cfg;
5504     
5505         
5506     }
5507     
5508     
5509     
5510 });
5511
5512
5513
5514  
5515
5516  
5517        /*
5518  * - LGPL
5519  *
5520  * navbar
5521  * navbar-fixed-top
5522  * navbar-expand-md  fixed-top 
5523  */
5524
5525 /**
5526  * @class Roo.bootstrap.NavHeaderbar
5527  * @extends Roo.bootstrap.NavSimplebar
5528  * Bootstrap Sidebar class
5529  *
5530  * @cfg {String} brand what is brand
5531  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5532  * @cfg {String} brand_href href of the brand
5533  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5534  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5535  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5536  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5537  * 
5538  * @constructor
5539  * Create a new Sidebar
5540  * @param {Object} config The config object
5541  */
5542
5543
5544 Roo.bootstrap.NavHeaderbar = function(config){
5545     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5546       
5547 };
5548
5549 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5550     
5551     position: '',
5552     brand: '',
5553     brand_href: false,
5554     srButton : true,
5555     autohide : false,
5556     desktopCenter : false,
5557    
5558     
5559     getAutoCreate : function(){
5560         
5561         var   cfg = {
5562             tag: this.nav || 'nav',
5563             cls: 'navbar navbar-expand-md',
5564             role: 'navigation',
5565             cn: []
5566         };
5567         
5568         var cn = cfg.cn;
5569         if (this.desktopCenter) {
5570             cn.push({cls : 'container', cn : []});
5571             cn = cn[0].cn;
5572         }
5573         
5574         if(this.srButton){
5575             var btn = {
5576                 tag: 'button',
5577                 type: 'button',
5578                 cls: 'navbar-toggle navbar-toggler',
5579                 'data-toggle': 'collapse',
5580                 cn: [
5581                     {
5582                         tag: 'span',
5583                         cls: 'sr-only',
5584                         html: 'Toggle navigation'
5585                     },
5586                     {
5587                         tag: 'span',
5588                         cls: 'icon-bar navbar-toggler-icon'
5589                     },
5590                     {
5591                         tag: 'span',
5592                         cls: 'icon-bar'
5593                     },
5594                     {
5595                         tag: 'span',
5596                         cls: 'icon-bar'
5597                     }
5598                 ]
5599             };
5600             
5601             cn.push( Roo.bootstrap.version == 4 ? btn : {
5602                 tag: 'div',
5603                 cls: 'navbar-header',
5604                 cn: [
5605                     btn
5606                 ]
5607             });
5608         }
5609         
5610         cn.push({
5611             tag: 'div',
5612             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5613             cn : []
5614         });
5615         
5616         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5617         
5618         if (['light','white'].indexOf(this.weight) > -1) {
5619             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5620         }
5621         cfg.cls += ' bg-' + this.weight;
5622         
5623         
5624         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5625             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5626             
5627             // tag can override this..
5628             
5629             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5630         }
5631         
5632         if (this.brand !== '') {
5633             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5634             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5635                 tag: 'a',
5636                 href: this.brand_href ? this.brand_href : '#',
5637                 cls: 'navbar-brand',
5638                 cn: [
5639                 this.brand
5640                 ]
5641             });
5642         }
5643         
5644         if(this.main){
5645             cfg.cls += ' main-nav';
5646         }
5647         
5648         
5649         return cfg;
5650
5651         
5652     },
5653     getHeaderChildContainer : function()
5654     {
5655         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5656             return this.el.select('.navbar-header',true).first();
5657         }
5658         
5659         return this.getChildContainer();
5660     },
5661     
5662     getChildContainer : function()
5663     {
5664          
5665         return this.el.select('.roo-navbar-collapse',true).first();
5666          
5667         
5668     },
5669     
5670     initEvents : function()
5671     {
5672         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5673         
5674         if (this.autohide) {
5675             
5676             var prevScroll = 0;
5677             var ft = this.el;
5678             
5679             Roo.get(document).on('scroll',function(e) {
5680                 var ns = Roo.get(document).getScroll().top;
5681                 var os = prevScroll;
5682                 prevScroll = ns;
5683                 
5684                 if(ns > os){
5685                     ft.removeClass('slideDown');
5686                     ft.addClass('slideUp');
5687                     return;
5688                 }
5689                 ft.removeClass('slideUp');
5690                 ft.addClass('slideDown');
5691                  
5692               
5693           },this);
5694         }
5695     }    
5696     
5697 });
5698
5699
5700
5701  
5702
5703  /*
5704  * - LGPL
5705  *
5706  * navbar
5707  * 
5708  */
5709
5710 /**
5711  * @class Roo.bootstrap.NavSidebar
5712  * @extends Roo.bootstrap.Navbar
5713  * Bootstrap Sidebar class
5714  * 
5715  * @constructor
5716  * Create a new Sidebar
5717  * @param {Object} config The config object
5718  */
5719
5720
5721 Roo.bootstrap.NavSidebar = function(config){
5722     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5723 };
5724
5725 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5726     
5727     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5728     
5729     getAutoCreate : function(){
5730         
5731         
5732         return  {
5733             tag: 'div',
5734             cls: 'sidebar sidebar-nav'
5735         };
5736     
5737         
5738     }
5739     
5740     
5741     
5742 });
5743
5744
5745
5746  
5747
5748  /*
5749  * - LGPL
5750  *
5751  * nav group
5752  * 
5753  */
5754
5755 /**
5756  * @class Roo.bootstrap.NavGroup
5757  * @extends Roo.bootstrap.Component
5758  * Bootstrap NavGroup class
5759  * @cfg {String} align (left|right)
5760  * @cfg {Boolean} inverse
5761  * @cfg {String} type (nav|pills|tab) default nav
5762  * @cfg {String} navId - reference Id for navbar.
5763  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5764  * 
5765  * @constructor
5766  * Create a new nav group
5767  * @param {Object} config The config object
5768  */
5769
5770 Roo.bootstrap.NavGroup = function(config){
5771     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5772     this.navItems = [];
5773    
5774     Roo.bootstrap.NavGroup.register(this);
5775      this.addEvents({
5776         /**
5777              * @event changed
5778              * Fires when the active item changes
5779              * @param {Roo.bootstrap.NavGroup} this
5780              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5781              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5782          */
5783         'changed': true
5784      });
5785     
5786 };
5787
5788 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5789     
5790     align: '',
5791     inverse: false,
5792     form: false,
5793     type: 'nav',
5794     navId : '',
5795     // private
5796     pilltype : true,
5797     
5798     navItems : false, 
5799     
5800     getAutoCreate : function()
5801     {
5802         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5803         
5804         cfg = {
5805             tag : 'ul',
5806             cls: 'nav' 
5807         };
5808         if (Roo.bootstrap.version == 4) {
5809             if (['tabs','pills'].indexOf(this.type) != -1) {
5810                 cfg.cls += ' nav-' + this.type; 
5811             } else {
5812                 // trying to remove so header bar can right align top?
5813                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5814                     // do not use on header bar... 
5815                     cfg.cls += ' navbar-nav';
5816                 }
5817             }
5818             
5819         } else {
5820             if (['tabs','pills'].indexOf(this.type) != -1) {
5821                 cfg.cls += ' nav-' + this.type
5822             } else {
5823                 if (this.type !== 'nav') {
5824                     Roo.log('nav type must be nav/tabs/pills')
5825                 }
5826                 cfg.cls += ' navbar-nav'
5827             }
5828         }
5829         
5830         if (this.parent() && this.parent().sidebar) {
5831             cfg = {
5832                 tag: 'ul',
5833                 cls: 'dashboard-menu sidebar-menu'
5834             };
5835             
5836             return cfg;
5837         }
5838         
5839         if (this.form === true) {
5840             cfg = {
5841                 tag: 'form',
5842                 cls: 'navbar-form form-inline'
5843             };
5844             //nav navbar-right ml-md-auto
5845             if (this.align === 'right') {
5846                 cfg.cls += ' navbar-right ml-md-auto';
5847             } else {
5848                 cfg.cls += ' navbar-left';
5849             }
5850         }
5851         
5852         if (this.align === 'right') {
5853             cfg.cls += ' navbar-right ml-md-auto';
5854         } else {
5855             cfg.cls += ' mr-auto';
5856         }
5857         
5858         if (this.inverse) {
5859             cfg.cls += ' navbar-inverse';
5860             
5861         }
5862         
5863         
5864         return cfg;
5865     },
5866     /**
5867     * sets the active Navigation item
5868     * @param {Roo.bootstrap.NavItem} the new current navitem
5869     */
5870     setActiveItem : function(item)
5871     {
5872         var prev = false;
5873         Roo.each(this.navItems, function(v){
5874             if (v == item) {
5875                 return ;
5876             }
5877             if (v.isActive()) {
5878                 v.setActive(false, true);
5879                 prev = v;
5880                 
5881             }
5882             
5883         });
5884
5885         item.setActive(true, true);
5886         this.fireEvent('changed', this, item, prev);
5887         
5888         
5889     },
5890     /**
5891     * gets the active Navigation item
5892     * @return {Roo.bootstrap.NavItem} the current navitem
5893     */
5894     getActive : function()
5895     {
5896         
5897         var prev = false;
5898         Roo.each(this.navItems, function(v){
5899             
5900             if (v.isActive()) {
5901                 prev = v;
5902                 
5903             }
5904             
5905         });
5906         return prev;
5907     },
5908     
5909     indexOfNav : function()
5910     {
5911         
5912         var prev = false;
5913         Roo.each(this.navItems, function(v,i){
5914             
5915             if (v.isActive()) {
5916                 prev = i;
5917                 
5918             }
5919             
5920         });
5921         return prev;
5922     },
5923     /**
5924     * adds a Navigation item
5925     * @param {Roo.bootstrap.NavItem} the navitem to add
5926     */
5927     addItem : function(cfg)
5928     {
5929         if (this.form && Roo.bootstrap.version == 4) {
5930             cfg.tag = 'div';
5931         }
5932         var cn = new Roo.bootstrap.NavItem(cfg);
5933         this.register(cn);
5934         cn.parentId = this.id;
5935         cn.onRender(this.el, null);
5936         return cn;
5937     },
5938     /**
5939     * register a Navigation item
5940     * @param {Roo.bootstrap.NavItem} the navitem to add
5941     */
5942     register : function(item)
5943     {
5944         this.navItems.push( item);
5945         item.navId = this.navId;
5946     
5947     },
5948     
5949     /**
5950     * clear all the Navigation item
5951     */
5952    
5953     clearAll : function()
5954     {
5955         this.navItems = [];
5956         this.el.dom.innerHTML = '';
5957     },
5958     
5959     getNavItem: function(tabId)
5960     {
5961         var ret = false;
5962         Roo.each(this.navItems, function(e) {
5963             if (e.tabId == tabId) {
5964                ret =  e;
5965                return false;
5966             }
5967             return true;
5968             
5969         });
5970         return ret;
5971     },
5972     
5973     setActiveNext : function()
5974     {
5975         var i = this.indexOfNav(this.getActive());
5976         if (i > this.navItems.length) {
5977             return;
5978         }
5979         this.setActiveItem(this.navItems[i+1]);
5980     },
5981     setActivePrev : function()
5982     {
5983         var i = this.indexOfNav(this.getActive());
5984         if (i  < 1) {
5985             return;
5986         }
5987         this.setActiveItem(this.navItems[i-1]);
5988     },
5989     clearWasActive : function(except) {
5990         Roo.each(this.navItems, function(e) {
5991             if (e.tabId != except.tabId && e.was_active) {
5992                e.was_active = false;
5993                return false;
5994             }
5995             return true;
5996             
5997         });
5998     },
5999     getWasActive : function ()
6000     {
6001         var r = false;
6002         Roo.each(this.navItems, function(e) {
6003             if (e.was_active) {
6004                r = e;
6005                return false;
6006             }
6007             return true;
6008             
6009         });
6010         return r;
6011     }
6012     
6013     
6014 });
6015
6016  
6017 Roo.apply(Roo.bootstrap.NavGroup, {
6018     
6019     groups: {},
6020      /**
6021     * register a Navigation Group
6022     * @param {Roo.bootstrap.NavGroup} the navgroup to add
6023     */
6024     register : function(navgrp)
6025     {
6026         this.groups[navgrp.navId] = navgrp;
6027         
6028     },
6029     /**
6030     * fetch a Navigation Group based on the navigation ID
6031     * @param {string} the navgroup to add
6032     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6033     */
6034     get: function(navId) {
6035         if (typeof(this.groups[navId]) == 'undefined') {
6036             return false;
6037             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6038         }
6039         return this.groups[navId] ;
6040     }
6041     
6042     
6043     
6044 });
6045
6046  /*
6047  * - LGPL
6048  *
6049  * row
6050  * 
6051  */
6052
6053 /**
6054  * @class Roo.bootstrap.NavItem
6055  * @extends Roo.bootstrap.Component
6056  * Bootstrap Navbar.NavItem class
6057  * @cfg {String} href  link to
6058  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6059  * @cfg {Boolean} button_outline show and outlined button
6060  * @cfg {String} html content of button
6061  * @cfg {String} badge text inside badge
6062  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6063  * @cfg {String} glyphicon DEPRICATED - use fa
6064  * @cfg {String} icon DEPRICATED - use fa
6065  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6066  * @cfg {Boolean} active Is item active
6067  * @cfg {Boolean} disabled Is item disabled
6068  * @cfg {String} linkcls  Link Class
6069  * @cfg {Boolean} preventDefault (true | false) default false
6070  * @cfg {String} tabId the tab that this item activates.
6071  * @cfg {String} tagtype (a|span) render as a href or span?
6072  * @cfg {Boolean} animateRef (true|false) link to element default false  
6073   
6074  * @constructor
6075  * Create a new Navbar Item
6076  * @param {Object} config The config object
6077  */
6078 Roo.bootstrap.NavItem = function(config){
6079     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6080     this.addEvents({
6081         // raw events
6082         /**
6083          * @event click
6084          * The raw click event for the entire grid.
6085          * @param {Roo.EventObject} e
6086          */
6087         "click" : true,
6088          /**
6089             * @event changed
6090             * Fires when the active item active state changes
6091             * @param {Roo.bootstrap.NavItem} this
6092             * @param {boolean} state the new state
6093              
6094          */
6095         'changed': true,
6096         /**
6097             * @event scrollto
6098             * Fires when scroll to element
6099             * @param {Roo.bootstrap.NavItem} this
6100             * @param {Object} options
6101             * @param {Roo.EventObject} e
6102              
6103          */
6104         'scrollto': true
6105     });
6106    
6107 };
6108
6109 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6110     
6111     href: false,
6112     html: '',
6113     badge: '',
6114     icon: false,
6115     fa : false,
6116     glyphicon: false,
6117     active: false,
6118     preventDefault : false,
6119     tabId : false,
6120     tagtype : 'a',
6121     tag: 'li',
6122     disabled : false,
6123     animateRef : false,
6124     was_active : false,
6125     button_weight : '',
6126     button_outline : false,
6127     linkcls : '',
6128     navLink: false,
6129     
6130     getAutoCreate : function(){
6131          
6132         var cfg = {
6133             tag: this.tag,
6134             cls: 'nav-item'
6135         };
6136         
6137         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6138         
6139         if (this.active) {
6140             cfg.cls +=  ' active' ;
6141         }
6142         if (this.disabled) {
6143             cfg.cls += ' disabled';
6144         }
6145         
6146         // BS4 only?
6147         if (this.button_weight.length) {
6148             cfg.tag = this.href ? 'a' : 'button';
6149             cfg.html = this.html || '';
6150             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6151             if (this.href) {
6152                 cfg.href = this.href;
6153             }
6154             if (this.fa) {
6155                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6156             }
6157             
6158             // menu .. should add dropdown-menu class - so no need for carat..
6159             
6160             if (this.badge !== '') {
6161                  
6162                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6163             }
6164             return cfg;
6165         }
6166         
6167         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6168             cfg.cn = [
6169                 {
6170                     tag: this.tagtype,
6171                     href : this.href || "#",
6172                     html: this.html || ''
6173                 }
6174             ];
6175             if (this.tagtype == 'a') {
6176                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6177         
6178             }
6179             if (this.icon) {
6180                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6181             }
6182             if (this.fa) {
6183                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6184             }
6185             if(this.glyphicon) {
6186                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6187             }
6188             
6189             if (this.menu) {
6190                 
6191                 cfg.cn[0].html += " <span class='caret'></span>";
6192              
6193             }
6194             
6195             if (this.badge !== '') {
6196                  
6197                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6198             }
6199         }
6200         
6201         
6202         
6203         return cfg;
6204     },
6205     onRender : function(ct, position)
6206     {
6207        // Roo.log("Call onRender: " + this.xtype);
6208         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6209             this.tag = 'div';
6210         }
6211         
6212         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6213         this.navLink = this.el.select('.nav-link',true).first();
6214         return ret;
6215     },
6216       
6217     
6218     initEvents: function() 
6219     {
6220         if (typeof (this.menu) != 'undefined') {
6221             this.menu.parentType = this.xtype;
6222             this.menu.triggerEl = this.el;
6223             this.menu = this.addxtype(Roo.apply({}, this.menu));
6224         }
6225         
6226         this.el.on('click', this.onClick, this);
6227         
6228         //if(this.tagtype == 'span'){
6229         //    this.el.select('span',true).on('click', this.onClick, this);
6230         //}
6231        
6232         // at this point parent should be available..
6233         this.parent().register(this);
6234     },
6235     
6236     onClick : function(e)
6237     {
6238         if (e.getTarget('.dropdown-menu-item')) {
6239             // did you click on a menu itemm.... - then don't trigger onclick..
6240             return;
6241         }
6242         
6243         if(
6244                 this.preventDefault || 
6245                 this.href == '#' 
6246         ){
6247             Roo.log("NavItem - prevent Default?");
6248             e.preventDefault();
6249         }
6250         
6251         if (this.disabled) {
6252             return;
6253         }
6254         
6255         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6256         if (tg && tg.transition) {
6257             Roo.log("waiting for the transitionend");
6258             return;
6259         }
6260         
6261         
6262         
6263         //Roo.log("fire event clicked");
6264         if(this.fireEvent('click', this, e) === false){
6265             return;
6266         };
6267         
6268         if(this.tagtype == 'span'){
6269             return;
6270         }
6271         
6272         //Roo.log(this.href);
6273         var ael = this.el.select('a',true).first();
6274         //Roo.log(ael);
6275         
6276         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6277             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6278             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6279                 return; // ignore... - it's a 'hash' to another page.
6280             }
6281             Roo.log("NavItem - prevent Default?");
6282             e.preventDefault();
6283             this.scrollToElement(e);
6284         }
6285         
6286         
6287         var p =  this.parent();
6288    
6289         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6290             if (typeof(p.setActiveItem) !== 'undefined') {
6291                 p.setActiveItem(this);
6292             }
6293         }
6294         
6295         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6296         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6297             // remove the collapsed menu expand...
6298             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6299         }
6300     },
6301     
6302     isActive: function () {
6303         return this.active
6304     },
6305     setActive : function(state, fire, is_was_active)
6306     {
6307         if (this.active && !state && this.navId) {
6308             this.was_active = true;
6309             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6310             if (nv) {
6311                 nv.clearWasActive(this);
6312             }
6313             
6314         }
6315         this.active = state;
6316         
6317         if (!state ) {
6318             this.el.removeClass('active');
6319             this.navLink ? this.navLink.removeClass('active') : false;
6320         } else if (!this.el.hasClass('active')) {
6321             
6322             this.el.addClass('active');
6323             if (Roo.bootstrap.version == 4 && this.navLink ) {
6324                 this.navLink.addClass('active');
6325             }
6326             
6327         }
6328         if (fire) {
6329             this.fireEvent('changed', this, state);
6330         }
6331         
6332         // show a panel if it's registered and related..
6333         
6334         if (!this.navId || !this.tabId || !state || is_was_active) {
6335             return;
6336         }
6337         
6338         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6339         if (!tg) {
6340             return;
6341         }
6342         var pan = tg.getPanelByName(this.tabId);
6343         if (!pan) {
6344             return;
6345         }
6346         // if we can not flip to new panel - go back to old nav highlight..
6347         if (false == tg.showPanel(pan)) {
6348             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6349             if (nv) {
6350                 var onav = nv.getWasActive();
6351                 if (onav) {
6352                     onav.setActive(true, false, true);
6353                 }
6354             }
6355             
6356         }
6357         
6358         
6359         
6360     },
6361      // this should not be here...
6362     setDisabled : function(state)
6363     {
6364         this.disabled = state;
6365         if (!state ) {
6366             this.el.removeClass('disabled');
6367         } else if (!this.el.hasClass('disabled')) {
6368             this.el.addClass('disabled');
6369         }
6370         
6371     },
6372     
6373     /**
6374      * Fetch the element to display the tooltip on.
6375      * @return {Roo.Element} defaults to this.el
6376      */
6377     tooltipEl : function()
6378     {
6379         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6380     },
6381     
6382     scrollToElement : function(e)
6383     {
6384         var c = document.body;
6385         
6386         /*
6387          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6388          */
6389         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6390             c = document.documentElement;
6391         }
6392         
6393         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6394         
6395         if(!target){
6396             return;
6397         }
6398
6399         var o = target.calcOffsetsTo(c);
6400         
6401         var options = {
6402             target : target,
6403             value : o[1]
6404         };
6405         
6406         this.fireEvent('scrollto', this, options, e);
6407         
6408         Roo.get(c).scrollTo('top', options.value, true);
6409         
6410         return;
6411     }
6412 });
6413  
6414
6415  /*
6416  * - LGPL
6417  *
6418  * sidebar item
6419  *
6420  *  li
6421  *    <span> icon </span>
6422  *    <span> text </span>
6423  *    <span>badge </span>
6424  */
6425
6426 /**
6427  * @class Roo.bootstrap.NavSidebarItem
6428  * @extends Roo.bootstrap.NavItem
6429  * Bootstrap Navbar.NavSidebarItem class
6430  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6431  * {Boolean} open is the menu open
6432  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6433  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6434  * {String} buttonSize (sm|md|lg)the extra classes for the button
6435  * {Boolean} showArrow show arrow next to the text (default true)
6436  * @constructor
6437  * Create a new Navbar Button
6438  * @param {Object} config The config object
6439  */
6440 Roo.bootstrap.NavSidebarItem = function(config){
6441     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6442     this.addEvents({
6443         // raw events
6444         /**
6445          * @event click
6446          * The raw click event for the entire grid.
6447          * @param {Roo.EventObject} e
6448          */
6449         "click" : true,
6450          /**
6451             * @event changed
6452             * Fires when the active item active state changes
6453             * @param {Roo.bootstrap.NavSidebarItem} this
6454             * @param {boolean} state the new state
6455              
6456          */
6457         'changed': true
6458     });
6459    
6460 };
6461
6462 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6463     
6464     badgeWeight : 'default',
6465     
6466     open: false,
6467     
6468     buttonView : false,
6469     
6470     buttonWeight : 'default',
6471     
6472     buttonSize : 'md',
6473     
6474     showArrow : true,
6475     
6476     getAutoCreate : function(){
6477         
6478         
6479         var a = {
6480                 tag: 'a',
6481                 href : this.href || '#',
6482                 cls: '',
6483                 html : '',
6484                 cn : []
6485         };
6486         
6487         if(this.buttonView){
6488             a = {
6489                 tag: 'button',
6490                 href : this.href || '#',
6491                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6492                 html : this.html,
6493                 cn : []
6494             };
6495         }
6496         
6497         var cfg = {
6498             tag: 'li',
6499             cls: '',
6500             cn: [ a ]
6501         };
6502         
6503         if (this.active) {
6504             cfg.cls += ' active';
6505         }
6506         
6507         if (this.disabled) {
6508             cfg.cls += ' disabled';
6509         }
6510         if (this.open) {
6511             cfg.cls += ' open x-open';
6512         }
6513         // left icon..
6514         if (this.glyphicon || this.icon) {
6515             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6516             a.cn.push({ tag : 'i', cls : c }) ;
6517         }
6518         
6519         if(!this.buttonView){
6520             var span = {
6521                 tag: 'span',
6522                 html : this.html || ''
6523             };
6524
6525             a.cn.push(span);
6526             
6527         }
6528         
6529         if (this.badge !== '') {
6530             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6531         }
6532         
6533         if (this.menu) {
6534             
6535             if(this.showArrow){
6536                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6537             }
6538             
6539             a.cls += ' dropdown-toggle treeview' ;
6540         }
6541         
6542         return cfg;
6543     },
6544     
6545     initEvents : function()
6546     { 
6547         if (typeof (this.menu) != 'undefined') {
6548             this.menu.parentType = this.xtype;
6549             this.menu.triggerEl = this.el;
6550             this.menu = this.addxtype(Roo.apply({}, this.menu));
6551         }
6552         
6553         this.el.on('click', this.onClick, this);
6554         
6555         if(this.badge !== ''){
6556             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6557         }
6558         
6559     },
6560     
6561     onClick : function(e)
6562     {
6563         if(this.disabled){
6564             e.preventDefault();
6565             return;
6566         }
6567         
6568         if(this.preventDefault){
6569             e.preventDefault();
6570         }
6571         
6572         this.fireEvent('click', this, e);
6573     },
6574     
6575     disable : function()
6576     {
6577         this.setDisabled(true);
6578     },
6579     
6580     enable : function()
6581     {
6582         this.setDisabled(false);
6583     },
6584     
6585     setDisabled : function(state)
6586     {
6587         if(this.disabled == state){
6588             return;
6589         }
6590         
6591         this.disabled = state;
6592         
6593         if (state) {
6594             this.el.addClass('disabled');
6595             return;
6596         }
6597         
6598         this.el.removeClass('disabled');
6599         
6600         return;
6601     },
6602     
6603     setActive : function(state)
6604     {
6605         if(this.active == state){
6606             return;
6607         }
6608         
6609         this.active = state;
6610         
6611         if (state) {
6612             this.el.addClass('active');
6613             return;
6614         }
6615         
6616         this.el.removeClass('active');
6617         
6618         return;
6619     },
6620     
6621     isActive: function () 
6622     {
6623         return this.active;
6624     },
6625     
6626     setBadge : function(str)
6627     {
6628         if(!this.badgeEl){
6629             return;
6630         }
6631         
6632         this.badgeEl.dom.innerHTML = str;
6633     }
6634     
6635    
6636      
6637  
6638 });
6639  
6640
6641  /*
6642  * - LGPL
6643  *
6644  *  Breadcrumb Nav
6645  * 
6646  */
6647 Roo.namespace('Roo.bootstrap.breadcrumb');
6648
6649
6650 /**
6651  * @class Roo.bootstrap.breadcrumb.Nav
6652  * @extends Roo.bootstrap.Component
6653  * Bootstrap Breadcrumb Nav Class
6654  *  
6655  * @children Roo.bootstrap.breadcrumb.Item
6656  * 
6657  * @constructor
6658  * Create a new breadcrumb.Nav
6659  * @param {Object} config The config object
6660  */
6661
6662
6663 Roo.bootstrap.breadcrumb.Nav = function(config){
6664     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6665     
6666     
6667 };
6668
6669 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6670     
6671     getAutoCreate : function()
6672     {
6673
6674         var cfg = {
6675             tag: 'nav',
6676             cn : [
6677                 {
6678                     tag : 'ol',
6679                     cls : 'breadcrumb'
6680                 }
6681             ]
6682             
6683         };
6684           
6685         return cfg;
6686     },
6687     
6688     initEvents: function()
6689     {
6690         this.olEl = this.el.select('ol',true).first();    
6691     },
6692     getChildContainer : function()
6693     {
6694         return this.olEl;  
6695     }
6696     
6697 });
6698
6699  /*
6700  * - LGPL
6701  *
6702  *  Breadcrumb Item
6703  * 
6704  */
6705
6706
6707 /**
6708  * @class Roo.bootstrap.breadcrumb.Nav
6709  * @extends Roo.bootstrap.Component
6710  * Bootstrap Breadcrumb Nav Class
6711  *  
6712  * @children Roo.bootstrap.breadcrumb.Component
6713  * @cfg {String} html the content of the link.
6714  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6715  * @cfg {Boolean} active is it active
6716
6717  * 
6718  * @constructor
6719  * Create a new breadcrumb.Nav
6720  * @param {Object} config The config object
6721  */
6722
6723 Roo.bootstrap.breadcrumb.Item = function(config){
6724     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6725     this.addEvents({
6726         // img events
6727         /**
6728          * @event click
6729          * The img click event for the img.
6730          * @param {Roo.EventObject} e
6731          */
6732         "click" : true
6733     });
6734     
6735 };
6736
6737 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6738     
6739     href: false,
6740     html : '',
6741     
6742     getAutoCreate : function()
6743     {
6744
6745         var cfg = {
6746             tag: 'li',
6747             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6748         };
6749         if (this.href !== false) {
6750             cfg.cn = [{
6751                 tag : 'a',
6752                 href : this.href,
6753                 html : this.html
6754             }];
6755         } else {
6756             cfg.html = this.html;
6757         }
6758         
6759         return cfg;
6760     },
6761     
6762     initEvents: function()
6763     {
6764         if (this.href) {
6765             this.el.select('a', true).first().on('click',this.onClick, this)
6766         }
6767         
6768     },
6769     onClick : function(e)
6770     {
6771         e.preventDefault();
6772         this.fireEvent('click',this,  e);
6773     }
6774     
6775 });
6776
6777  /*
6778  * - LGPL
6779  *
6780  * row
6781  * 
6782  */
6783
6784 /**
6785  * @class Roo.bootstrap.Row
6786  * @extends Roo.bootstrap.Component
6787  * Bootstrap Row class (contains columns...)
6788  * 
6789  * @constructor
6790  * Create a new Row
6791  * @param {Object} config The config object
6792  */
6793
6794 Roo.bootstrap.Row = function(config){
6795     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6796 };
6797
6798 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6799     
6800     getAutoCreate : function(){
6801        return {
6802             cls: 'row clearfix'
6803        };
6804     }
6805     
6806     
6807 });
6808
6809  
6810
6811  /*
6812  * - LGPL
6813  *
6814  * pagination
6815  * 
6816  */
6817
6818 /**
6819  * @class Roo.bootstrap.Pagination
6820  * @extends Roo.bootstrap.Component
6821  * Bootstrap Pagination class
6822  * @cfg {String} size xs | sm | md | lg
6823  * @cfg {Boolean} inverse false | true
6824  * 
6825  * @constructor
6826  * Create a new Pagination
6827  * @param {Object} config The config object
6828  */
6829
6830 Roo.bootstrap.Pagination = function(config){
6831     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6832 };
6833
6834 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6835     
6836     cls: false,
6837     size: false,
6838     inverse: false,
6839     
6840     getAutoCreate : function(){
6841         var cfg = {
6842             tag: 'ul',
6843                 cls: 'pagination'
6844         };
6845         if (this.inverse) {
6846             cfg.cls += ' inverse';
6847         }
6848         if (this.html) {
6849             cfg.html=this.html;
6850         }
6851         if (this.cls) {
6852             cfg.cls += " " + this.cls;
6853         }
6854         return cfg;
6855     }
6856    
6857 });
6858
6859  
6860
6861  /*
6862  * - LGPL
6863  *
6864  * Pagination item
6865  * 
6866  */
6867
6868
6869 /**
6870  * @class Roo.bootstrap.PaginationItem
6871  * @extends Roo.bootstrap.Component
6872  * Bootstrap PaginationItem class
6873  * @cfg {String} html text
6874  * @cfg {String} href the link
6875  * @cfg {Boolean} preventDefault (true | false) default true
6876  * @cfg {Boolean} active (true | false) default false
6877  * @cfg {Boolean} disabled default false
6878  * 
6879  * 
6880  * @constructor
6881  * Create a new PaginationItem
6882  * @param {Object} config The config object
6883  */
6884
6885
6886 Roo.bootstrap.PaginationItem = function(config){
6887     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6888     this.addEvents({
6889         // raw events
6890         /**
6891          * @event click
6892          * The raw click event for the entire grid.
6893          * @param {Roo.EventObject} e
6894          */
6895         "click" : true
6896     });
6897 };
6898
6899 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6900     
6901     href : false,
6902     html : false,
6903     preventDefault: true,
6904     active : false,
6905     cls : false,
6906     disabled: false,
6907     
6908     getAutoCreate : function(){
6909         var cfg= {
6910             tag: 'li',
6911             cn: [
6912                 {
6913                     tag : 'a',
6914                     href : this.href ? this.href : '#',
6915                     html : this.html ? this.html : ''
6916                 }
6917             ]
6918         };
6919         
6920         if(this.cls){
6921             cfg.cls = this.cls;
6922         }
6923         
6924         if(this.disabled){
6925             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6926         }
6927         
6928         if(this.active){
6929             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6930         }
6931         
6932         return cfg;
6933     },
6934     
6935     initEvents: function() {
6936         
6937         this.el.on('click', this.onClick, this);
6938         
6939     },
6940     onClick : function(e)
6941     {
6942         Roo.log('PaginationItem on click ');
6943         if(this.preventDefault){
6944             e.preventDefault();
6945         }
6946         
6947         if(this.disabled){
6948             return;
6949         }
6950         
6951         this.fireEvent('click', this, e);
6952     }
6953    
6954 });
6955
6956  
6957
6958  /*
6959  * - LGPL
6960  *
6961  * slider
6962  * 
6963  */
6964
6965
6966 /**
6967  * @class Roo.bootstrap.Slider
6968  * @extends Roo.bootstrap.Component
6969  * Bootstrap Slider class
6970  *    
6971  * @constructor
6972  * Create a new Slider
6973  * @param {Object} config The config object
6974  */
6975
6976 Roo.bootstrap.Slider = function(config){
6977     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6978 };
6979
6980 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6981     
6982     getAutoCreate : function(){
6983         
6984         var cfg = {
6985             tag: 'div',
6986             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6987             cn: [
6988                 {
6989                     tag: 'a',
6990                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6991                 }
6992             ]
6993         };
6994         
6995         return cfg;
6996     }
6997    
6998 });
6999
7000  /*
7001  * Based on:
7002  * Ext JS Library 1.1.1
7003  * Copyright(c) 2006-2007, Ext JS, LLC.
7004  *
7005  * Originally Released Under LGPL - original licence link has changed is not relivant.
7006  *
7007  * Fork - LGPL
7008  * <script type="text/javascript">
7009  */
7010  
7011
7012 /**
7013  * @class Roo.grid.ColumnModel
7014  * @extends Roo.util.Observable
7015  * This is the default implementation of a ColumnModel used by the Grid. It defines
7016  * the columns in the grid.
7017  * <br>Usage:<br>
7018  <pre><code>
7019  var colModel = new Roo.grid.ColumnModel([
7020         {header: "Ticker", width: 60, sortable: true, locked: true},
7021         {header: "Company Name", width: 150, sortable: true},
7022         {header: "Market Cap.", width: 100, sortable: true},
7023         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7024         {header: "Employees", width: 100, sortable: true, resizable: false}
7025  ]);
7026  </code></pre>
7027  * <p>
7028  
7029  * The config options listed for this class are options which may appear in each
7030  * individual column definition.
7031  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7032  * @constructor
7033  * @param {Object} config An Array of column config objects. See this class's
7034  * config objects for details.
7035 */
7036 Roo.grid.ColumnModel = function(config){
7037         /**
7038      * The config passed into the constructor
7039      */
7040     this.config = config;
7041     this.lookup = {};
7042
7043     // if no id, create one
7044     // if the column does not have a dataIndex mapping,
7045     // map it to the order it is in the config
7046     for(var i = 0, len = config.length; i < len; i++){
7047         var c = config[i];
7048         if(typeof c.dataIndex == "undefined"){
7049             c.dataIndex = i;
7050         }
7051         if(typeof c.renderer == "string"){
7052             c.renderer = Roo.util.Format[c.renderer];
7053         }
7054         if(typeof c.id == "undefined"){
7055             c.id = Roo.id();
7056         }
7057         if(c.editor && c.editor.xtype){
7058             c.editor  = Roo.factory(c.editor, Roo.grid);
7059         }
7060         if(c.editor && c.editor.isFormField){
7061             c.editor = new Roo.grid.GridEditor(c.editor);
7062         }
7063         this.lookup[c.id] = c;
7064     }
7065
7066     /**
7067      * The width of columns which have no width specified (defaults to 100)
7068      * @type Number
7069      */
7070     this.defaultWidth = 100;
7071
7072     /**
7073      * Default sortable of columns which have no sortable specified (defaults to false)
7074      * @type Boolean
7075      */
7076     this.defaultSortable = false;
7077
7078     this.addEvents({
7079         /**
7080              * @event widthchange
7081              * Fires when the width of a column changes.
7082              * @param {ColumnModel} this
7083              * @param {Number} columnIndex The column index
7084              * @param {Number} newWidth The new width
7085              */
7086             "widthchange": true,
7087         /**
7088              * @event headerchange
7089              * Fires when the text of a header changes.
7090              * @param {ColumnModel} this
7091              * @param {Number} columnIndex The column index
7092              * @param {Number} newText The new header text
7093              */
7094             "headerchange": true,
7095         /**
7096              * @event hiddenchange
7097              * Fires when a column is hidden or "unhidden".
7098              * @param {ColumnModel} this
7099              * @param {Number} columnIndex The column index
7100              * @param {Boolean} hidden true if hidden, false otherwise
7101              */
7102             "hiddenchange": true,
7103             /**
7104          * @event columnmoved
7105          * Fires when a column is moved.
7106          * @param {ColumnModel} this
7107          * @param {Number} oldIndex
7108          * @param {Number} newIndex
7109          */
7110         "columnmoved" : true,
7111         /**
7112          * @event columlockchange
7113          * Fires when a column's locked state is changed
7114          * @param {ColumnModel} this
7115          * @param {Number} colIndex
7116          * @param {Boolean} locked true if locked
7117          */
7118         "columnlockchange" : true
7119     });
7120     Roo.grid.ColumnModel.superclass.constructor.call(this);
7121 };
7122 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7123     /**
7124      * @cfg {String} header The header text to display in the Grid view.
7125      */
7126     /**
7127      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7128      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7129      * specified, the column's index is used as an index into the Record's data Array.
7130      */
7131     /**
7132      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7133      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7134      */
7135     /**
7136      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7137      * Defaults to the value of the {@link #defaultSortable} property.
7138      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7139      */
7140     /**
7141      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7142      */
7143     /**
7144      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7145      */
7146     /**
7147      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7148      */
7149     /**
7150      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7151      */
7152     /**
7153      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7154      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7155      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7156      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7157      */
7158        /**
7159      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7160      */
7161     /**
7162      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7163      */
7164     /**
7165      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7166      */
7167     /**
7168      * @cfg {String} cursor (Optional)
7169      */
7170     /**
7171      * @cfg {String} tooltip (Optional)
7172      */
7173     /**
7174      * @cfg {Number} xs (Optional)
7175      */
7176     /**
7177      * @cfg {Number} sm (Optional)
7178      */
7179     /**
7180      * @cfg {Number} md (Optional)
7181      */
7182     /**
7183      * @cfg {Number} lg (Optional)
7184      */
7185     /**
7186      * Returns the id of the column at the specified index.
7187      * @param {Number} index The column index
7188      * @return {String} the id
7189      */
7190     getColumnId : function(index){
7191         return this.config[index].id;
7192     },
7193
7194     /**
7195      * Returns the column for a specified id.
7196      * @param {String} id The column id
7197      * @return {Object} the column
7198      */
7199     getColumnById : function(id){
7200         return this.lookup[id];
7201     },
7202
7203     
7204     /**
7205      * Returns the column for a specified dataIndex.
7206      * @param {String} dataIndex The column dataIndex
7207      * @return {Object|Boolean} the column or false if not found
7208      */
7209     getColumnByDataIndex: function(dataIndex){
7210         var index = this.findColumnIndex(dataIndex);
7211         return index > -1 ? this.config[index] : false;
7212     },
7213     
7214     /**
7215      * Returns the index for a specified column id.
7216      * @param {String} id The column id
7217      * @return {Number} the index, or -1 if not found
7218      */
7219     getIndexById : function(id){
7220         for(var i = 0, len = this.config.length; i < len; i++){
7221             if(this.config[i].id == id){
7222                 return i;
7223             }
7224         }
7225         return -1;
7226     },
7227     
7228     /**
7229      * Returns the index for a specified column dataIndex.
7230      * @param {String} dataIndex The column dataIndex
7231      * @return {Number} the index, or -1 if not found
7232      */
7233     
7234     findColumnIndex : function(dataIndex){
7235         for(var i = 0, len = this.config.length; i < len; i++){
7236             if(this.config[i].dataIndex == dataIndex){
7237                 return i;
7238             }
7239         }
7240         return -1;
7241     },
7242     
7243     
7244     moveColumn : function(oldIndex, newIndex){
7245         var c = this.config[oldIndex];
7246         this.config.splice(oldIndex, 1);
7247         this.config.splice(newIndex, 0, c);
7248         this.dataMap = null;
7249         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7250     },
7251
7252     isLocked : function(colIndex){
7253         return this.config[colIndex].locked === true;
7254     },
7255
7256     setLocked : function(colIndex, value, suppressEvent){
7257         if(this.isLocked(colIndex) == value){
7258             return;
7259         }
7260         this.config[colIndex].locked = value;
7261         if(!suppressEvent){
7262             this.fireEvent("columnlockchange", this, colIndex, value);
7263         }
7264     },
7265
7266     getTotalLockedWidth : function(){
7267         var totalWidth = 0;
7268         for(var i = 0; i < this.config.length; i++){
7269             if(this.isLocked(i) && !this.isHidden(i)){
7270                 this.totalWidth += this.getColumnWidth(i);
7271             }
7272         }
7273         return totalWidth;
7274     },
7275
7276     getLockedCount : function(){
7277         for(var i = 0, len = this.config.length; i < len; i++){
7278             if(!this.isLocked(i)){
7279                 return i;
7280             }
7281         }
7282         
7283         return this.config.length;
7284     },
7285
7286     /**
7287      * Returns the number of columns.
7288      * @return {Number}
7289      */
7290     getColumnCount : function(visibleOnly){
7291         if(visibleOnly === true){
7292             var c = 0;
7293             for(var i = 0, len = this.config.length; i < len; i++){
7294                 if(!this.isHidden(i)){
7295                     c++;
7296                 }
7297             }
7298             return c;
7299         }
7300         return this.config.length;
7301     },
7302
7303     /**
7304      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7305      * @param {Function} fn
7306      * @param {Object} scope (optional)
7307      * @return {Array} result
7308      */
7309     getColumnsBy : function(fn, scope){
7310         var r = [];
7311         for(var i = 0, len = this.config.length; i < len; i++){
7312             var c = this.config[i];
7313             if(fn.call(scope||this, c, i) === true){
7314                 r[r.length] = c;
7315             }
7316         }
7317         return r;
7318     },
7319
7320     /**
7321      * Returns true if the specified column is sortable.
7322      * @param {Number} col The column index
7323      * @return {Boolean}
7324      */
7325     isSortable : function(col){
7326         if(typeof this.config[col].sortable == "undefined"){
7327             return this.defaultSortable;
7328         }
7329         return this.config[col].sortable;
7330     },
7331
7332     /**
7333      * Returns the rendering (formatting) function defined for the column.
7334      * @param {Number} col The column index.
7335      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7336      */
7337     getRenderer : function(col){
7338         if(!this.config[col].renderer){
7339             return Roo.grid.ColumnModel.defaultRenderer;
7340         }
7341         return this.config[col].renderer;
7342     },
7343
7344     /**
7345      * Sets the rendering (formatting) function for a column.
7346      * @param {Number} col The column index
7347      * @param {Function} fn The function to use to process the cell's raw data
7348      * to return HTML markup for the grid view. The render function is called with
7349      * the following parameters:<ul>
7350      * <li>Data value.</li>
7351      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7352      * <li>css A CSS style string to apply to the table cell.</li>
7353      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7354      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7355      * <li>Row index</li>
7356      * <li>Column index</li>
7357      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7358      */
7359     setRenderer : function(col, fn){
7360         this.config[col].renderer = fn;
7361     },
7362
7363     /**
7364      * Returns the width for the specified column.
7365      * @param {Number} col The column index
7366      * @return {Number}
7367      */
7368     getColumnWidth : function(col){
7369         return this.config[col].width * 1 || this.defaultWidth;
7370     },
7371
7372     /**
7373      * Sets the width for a column.
7374      * @param {Number} col The column index
7375      * @param {Number} width The new width
7376      */
7377     setColumnWidth : function(col, width, suppressEvent){
7378         this.config[col].width = width;
7379         this.totalWidth = null;
7380         if(!suppressEvent){
7381              this.fireEvent("widthchange", this, col, width);
7382         }
7383     },
7384
7385     /**
7386      * Returns the total width of all columns.
7387      * @param {Boolean} includeHidden True to include hidden column widths
7388      * @return {Number}
7389      */
7390     getTotalWidth : function(includeHidden){
7391         if(!this.totalWidth){
7392             this.totalWidth = 0;
7393             for(var i = 0, len = this.config.length; i < len; i++){
7394                 if(includeHidden || !this.isHidden(i)){
7395                     this.totalWidth += this.getColumnWidth(i);
7396                 }
7397             }
7398         }
7399         return this.totalWidth;
7400     },
7401
7402     /**
7403      * Returns the header for the specified column.
7404      * @param {Number} col The column index
7405      * @return {String}
7406      */
7407     getColumnHeader : function(col){
7408         return this.config[col].header;
7409     },
7410
7411     /**
7412      * Sets the header for a column.
7413      * @param {Number} col The column index
7414      * @param {String} header The new header
7415      */
7416     setColumnHeader : function(col, header){
7417         this.config[col].header = header;
7418         this.fireEvent("headerchange", this, col, header);
7419     },
7420
7421     /**
7422      * Returns the tooltip for the specified column.
7423      * @param {Number} col The column index
7424      * @return {String}
7425      */
7426     getColumnTooltip : function(col){
7427             return this.config[col].tooltip;
7428     },
7429     /**
7430      * Sets the tooltip for a column.
7431      * @param {Number} col The column index
7432      * @param {String} tooltip The new tooltip
7433      */
7434     setColumnTooltip : function(col, tooltip){
7435             this.config[col].tooltip = tooltip;
7436     },
7437
7438     /**
7439      * Returns the dataIndex for the specified column.
7440      * @param {Number} col The column index
7441      * @return {Number}
7442      */
7443     getDataIndex : function(col){
7444         return this.config[col].dataIndex;
7445     },
7446
7447     /**
7448      * Sets the dataIndex for a column.
7449      * @param {Number} col The column index
7450      * @param {Number} dataIndex The new dataIndex
7451      */
7452     setDataIndex : function(col, dataIndex){
7453         this.config[col].dataIndex = dataIndex;
7454     },
7455
7456     
7457     
7458     /**
7459      * Returns true if the cell is editable.
7460      * @param {Number} colIndex The column index
7461      * @param {Number} rowIndex The row index - this is nto actually used..?
7462      * @return {Boolean}
7463      */
7464     isCellEditable : function(colIndex, rowIndex){
7465         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7466     },
7467
7468     /**
7469      * Returns the editor defined for the cell/column.
7470      * return false or null to disable editing.
7471      * @param {Number} colIndex The column index
7472      * @param {Number} rowIndex The row index
7473      * @return {Object}
7474      */
7475     getCellEditor : function(colIndex, rowIndex){
7476         return this.config[colIndex].editor;
7477     },
7478
7479     /**
7480      * Sets if a column is editable.
7481      * @param {Number} col The column index
7482      * @param {Boolean} editable True if the column is editable
7483      */
7484     setEditable : function(col, editable){
7485         this.config[col].editable = editable;
7486     },
7487
7488
7489     /**
7490      * Returns true if the column is hidden.
7491      * @param {Number} colIndex The column index
7492      * @return {Boolean}
7493      */
7494     isHidden : function(colIndex){
7495         return this.config[colIndex].hidden;
7496     },
7497
7498
7499     /**
7500      * Returns true if the column width cannot be changed
7501      */
7502     isFixed : function(colIndex){
7503         return this.config[colIndex].fixed;
7504     },
7505
7506     /**
7507      * Returns true if the column can be resized
7508      * @return {Boolean}
7509      */
7510     isResizable : function(colIndex){
7511         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7512     },
7513     /**
7514      * Sets if a column is hidden.
7515      * @param {Number} colIndex The column index
7516      * @param {Boolean} hidden True if the column is hidden
7517      */
7518     setHidden : function(colIndex, hidden){
7519         this.config[colIndex].hidden = hidden;
7520         this.totalWidth = null;
7521         this.fireEvent("hiddenchange", this, colIndex, hidden);
7522     },
7523
7524     /**
7525      * Sets the editor for a column.
7526      * @param {Number} col The column index
7527      * @param {Object} editor The editor object
7528      */
7529     setEditor : function(col, editor){
7530         this.config[col].editor = editor;
7531     }
7532 });
7533
7534 Roo.grid.ColumnModel.defaultRenderer = function(value)
7535 {
7536     if(typeof value == "object") {
7537         return value;
7538     }
7539         if(typeof value == "string" && value.length < 1){
7540             return "&#160;";
7541         }
7542     
7543         return String.format("{0}", value);
7544 };
7545
7546 // Alias for backwards compatibility
7547 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7548 /*
7549  * Based on:
7550  * Ext JS Library 1.1.1
7551  * Copyright(c) 2006-2007, Ext JS, LLC.
7552  *
7553  * Originally Released Under LGPL - original licence link has changed is not relivant.
7554  *
7555  * Fork - LGPL
7556  * <script type="text/javascript">
7557  */
7558  
7559 /**
7560  * @class Roo.LoadMask
7561  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7562  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7563  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7564  * element's UpdateManager load indicator and will be destroyed after the initial load.
7565  * @constructor
7566  * Create a new LoadMask
7567  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7568  * @param {Object} config The config object
7569  */
7570 Roo.LoadMask = function(el, config){
7571     this.el = Roo.get(el);
7572     Roo.apply(this, config);
7573     if(this.store){
7574         this.store.on('beforeload', this.onBeforeLoad, this);
7575         this.store.on('load', this.onLoad, this);
7576         this.store.on('loadexception', this.onLoadException, this);
7577         this.removeMask = false;
7578     }else{
7579         var um = this.el.getUpdateManager();
7580         um.showLoadIndicator = false; // disable the default indicator
7581         um.on('beforeupdate', this.onBeforeLoad, this);
7582         um.on('update', this.onLoad, this);
7583         um.on('failure', this.onLoad, this);
7584         this.removeMask = true;
7585     }
7586 };
7587
7588 Roo.LoadMask.prototype = {
7589     /**
7590      * @cfg {Boolean} removeMask
7591      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7592      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7593      */
7594     /**
7595      * @cfg {String} msg
7596      * The text to display in a centered loading message box (defaults to 'Loading...')
7597      */
7598     msg : 'Loading...',
7599     /**
7600      * @cfg {String} msgCls
7601      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7602      */
7603     msgCls : 'x-mask-loading',
7604
7605     /**
7606      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7607      * @type Boolean
7608      */
7609     disabled: false,
7610
7611     /**
7612      * Disables the mask to prevent it from being displayed
7613      */
7614     disable : function(){
7615        this.disabled = true;
7616     },
7617
7618     /**
7619      * Enables the mask so that it can be displayed
7620      */
7621     enable : function(){
7622         this.disabled = false;
7623     },
7624     
7625     onLoadException : function()
7626     {
7627         Roo.log(arguments);
7628         
7629         if (typeof(arguments[3]) != 'undefined') {
7630             Roo.MessageBox.alert("Error loading",arguments[3]);
7631         } 
7632         /*
7633         try {
7634             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7635                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7636             }   
7637         } catch(e) {
7638             
7639         }
7640         */
7641     
7642         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7643     },
7644     // private
7645     onLoad : function()
7646     {
7647         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7648     },
7649
7650     // private
7651     onBeforeLoad : function(){
7652         if(!this.disabled){
7653             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7654         }
7655     },
7656
7657     // private
7658     destroy : function(){
7659         if(this.store){
7660             this.store.un('beforeload', this.onBeforeLoad, this);
7661             this.store.un('load', this.onLoad, this);
7662             this.store.un('loadexception', this.onLoadException, this);
7663         }else{
7664             var um = this.el.getUpdateManager();
7665             um.un('beforeupdate', this.onBeforeLoad, this);
7666             um.un('update', this.onLoad, this);
7667             um.un('failure', this.onLoad, this);
7668         }
7669     }
7670 };/*
7671  * - LGPL
7672  *
7673  * table
7674  * 
7675  */
7676
7677 /**
7678  * @class Roo.bootstrap.Table
7679  * @extends Roo.bootstrap.Component
7680  * Bootstrap Table class
7681  * @cfg {String} cls table class
7682  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7683  * @cfg {String} bgcolor Specifies the background color for a table
7684  * @cfg {Number} border Specifies whether the table cells should have borders or not
7685  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7686  * @cfg {Number} cellspacing Specifies the space between cells
7687  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7688  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7689  * @cfg {String} sortable Specifies that the table should be sortable
7690  * @cfg {String} summary Specifies a summary of the content of a table
7691  * @cfg {Number} width Specifies the width of a table
7692  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7693  * 
7694  * @cfg {boolean} striped Should the rows be alternative striped
7695  * @cfg {boolean} bordered Add borders to the table
7696  * @cfg {boolean} hover Add hover highlighting
7697  * @cfg {boolean} condensed Format condensed
7698  * @cfg {boolean} responsive Format condensed
7699  * @cfg {Boolean} loadMask (true|false) default false
7700  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7701  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7702  * @cfg {Boolean} rowSelection (true|false) default false
7703  * @cfg {Boolean} cellSelection (true|false) default false
7704  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7705  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7706  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7707  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7708  
7709  * 
7710  * @constructor
7711  * Create a new Table
7712  * @param {Object} config The config object
7713  */
7714
7715 Roo.bootstrap.Table = function(config){
7716     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7717     
7718   
7719     
7720     // BC...
7721     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7722     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7723     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7724     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7725     
7726     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7727     if (this.sm) {
7728         this.sm.grid = this;
7729         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7730         this.sm = this.selModel;
7731         this.sm.xmodule = this.xmodule || false;
7732     }
7733     
7734     if (this.cm && typeof(this.cm.config) == 'undefined') {
7735         this.colModel = new Roo.grid.ColumnModel(this.cm);
7736         this.cm = this.colModel;
7737         this.cm.xmodule = this.xmodule || false;
7738     }
7739     if (this.store) {
7740         this.store= Roo.factory(this.store, Roo.data);
7741         this.ds = this.store;
7742         this.ds.xmodule = this.xmodule || false;
7743          
7744     }
7745     if (this.footer && this.store) {
7746         this.footer.dataSource = this.ds;
7747         this.footer = Roo.factory(this.footer);
7748     }
7749     
7750     /** @private */
7751     this.addEvents({
7752         /**
7753          * @event cellclick
7754          * Fires when a cell is clicked
7755          * @param {Roo.bootstrap.Table} this
7756          * @param {Roo.Element} el
7757          * @param {Number} rowIndex
7758          * @param {Number} columnIndex
7759          * @param {Roo.EventObject} e
7760          */
7761         "cellclick" : true,
7762         /**
7763          * @event celldblclick
7764          * Fires when a cell is double clicked
7765          * @param {Roo.bootstrap.Table} this
7766          * @param {Roo.Element} el
7767          * @param {Number} rowIndex
7768          * @param {Number} columnIndex
7769          * @param {Roo.EventObject} e
7770          */
7771         "celldblclick" : true,
7772         /**
7773          * @event rowclick
7774          * Fires when a row is clicked
7775          * @param {Roo.bootstrap.Table} this
7776          * @param {Roo.Element} el
7777          * @param {Number} rowIndex
7778          * @param {Roo.EventObject} e
7779          */
7780         "rowclick" : true,
7781         /**
7782          * @event rowdblclick
7783          * Fires when a row is double clicked
7784          * @param {Roo.bootstrap.Table} this
7785          * @param {Roo.Element} el
7786          * @param {Number} rowIndex
7787          * @param {Roo.EventObject} e
7788          */
7789         "rowdblclick" : true,
7790         /**
7791          * @event mouseover
7792          * Fires when a mouseover occur
7793          * @param {Roo.bootstrap.Table} this
7794          * @param {Roo.Element} el
7795          * @param {Number} rowIndex
7796          * @param {Number} columnIndex
7797          * @param {Roo.EventObject} e
7798          */
7799         "mouseover" : true,
7800         /**
7801          * @event mouseout
7802          * Fires when a mouseout occur
7803          * @param {Roo.bootstrap.Table} this
7804          * @param {Roo.Element} el
7805          * @param {Number} rowIndex
7806          * @param {Number} columnIndex
7807          * @param {Roo.EventObject} e
7808          */
7809         "mouseout" : true,
7810         /**
7811          * @event rowclass
7812          * Fires when a row is rendered, so you can change add a style to it.
7813          * @param {Roo.bootstrap.Table} this
7814          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7815          */
7816         'rowclass' : true,
7817           /**
7818          * @event rowsrendered
7819          * Fires when all the  rows have been rendered
7820          * @param {Roo.bootstrap.Table} this
7821          */
7822         'rowsrendered' : true,
7823         /**
7824          * @event contextmenu
7825          * The raw contextmenu event for the entire grid.
7826          * @param {Roo.EventObject} e
7827          */
7828         "contextmenu" : true,
7829         /**
7830          * @event rowcontextmenu
7831          * Fires when a row is right clicked
7832          * @param {Roo.bootstrap.Table} this
7833          * @param {Number} rowIndex
7834          * @param {Roo.EventObject} e
7835          */
7836         "rowcontextmenu" : true,
7837         /**
7838          * @event cellcontextmenu
7839          * Fires when a cell is right clicked
7840          * @param {Roo.bootstrap.Table} this
7841          * @param {Number} rowIndex
7842          * @param {Number} cellIndex
7843          * @param {Roo.EventObject} e
7844          */
7845          "cellcontextmenu" : true,
7846          /**
7847          * @event headercontextmenu
7848          * Fires when a header is right clicked
7849          * @param {Roo.bootstrap.Table} this
7850          * @param {Number} columnIndex
7851          * @param {Roo.EventObject} e
7852          */
7853         "headercontextmenu" : true
7854     });
7855 };
7856
7857 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7858     
7859     cls: false,
7860     align: false,
7861     bgcolor: false,
7862     border: false,
7863     cellpadding: false,
7864     cellspacing: false,
7865     frame: false,
7866     rules: false,
7867     sortable: false,
7868     summary: false,
7869     width: false,
7870     striped : false,
7871     scrollBody : false,
7872     bordered: false,
7873     hover:  false,
7874     condensed : false,
7875     responsive : false,
7876     sm : false,
7877     cm : false,
7878     store : false,
7879     loadMask : false,
7880     footerShow : true,
7881     headerShow : true,
7882   
7883     rowSelection : false,
7884     cellSelection : false,
7885     layout : false,
7886     
7887     // Roo.Element - the tbody
7888     mainBody: false,
7889     // Roo.Element - thead element
7890     mainHead: false,
7891     
7892     container: false, // used by gridpanel...
7893     
7894     lazyLoad : false,
7895     
7896     CSS : Roo.util.CSS,
7897     
7898     auto_hide_footer : false,
7899     
7900     getAutoCreate : function()
7901     {
7902         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7903         
7904         cfg = {
7905             tag: 'table',
7906             cls : 'table',
7907             cn : []
7908         };
7909         if (this.scrollBody) {
7910             cfg.cls += ' table-body-fixed';
7911         }    
7912         if (this.striped) {
7913             cfg.cls += ' table-striped';
7914         }
7915         
7916         if (this.hover) {
7917             cfg.cls += ' table-hover';
7918         }
7919         if (this.bordered) {
7920             cfg.cls += ' table-bordered';
7921         }
7922         if (this.condensed) {
7923             cfg.cls += ' table-condensed';
7924         }
7925         if (this.responsive) {
7926             cfg.cls += ' table-responsive';
7927         }
7928         
7929         if (this.cls) {
7930             cfg.cls+=  ' ' +this.cls;
7931         }
7932         
7933         // this lot should be simplifed...
7934         var _t = this;
7935         var cp = [
7936             'align',
7937             'bgcolor',
7938             'border',
7939             'cellpadding',
7940             'cellspacing',
7941             'frame',
7942             'rules',
7943             'sortable',
7944             'summary',
7945             'width'
7946         ].forEach(function(k) {
7947             if (_t[k]) {
7948                 cfg[k] = _t[k];
7949             }
7950         });
7951         
7952         
7953         if (this.layout) {
7954             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7955         }
7956         
7957         if(this.store || this.cm){
7958             if(this.headerShow){
7959                 cfg.cn.push(this.renderHeader());
7960             }
7961             
7962             cfg.cn.push(this.renderBody());
7963             
7964             if(this.footerShow){
7965                 cfg.cn.push(this.renderFooter());
7966             }
7967             // where does this come from?
7968             //cfg.cls+=  ' TableGrid';
7969         }
7970         
7971         return { cn : [ cfg ] };
7972     },
7973     
7974     initEvents : function()
7975     {   
7976         if(!this.store || !this.cm){
7977             return;
7978         }
7979         if (this.selModel) {
7980             this.selModel.initEvents();
7981         }
7982         
7983         
7984         //Roo.log('initEvents with ds!!!!');
7985         
7986         this.mainBody = this.el.select('tbody', true).first();
7987         this.mainHead = this.el.select('thead', true).first();
7988         this.mainFoot = this.el.select('tfoot', true).first();
7989         
7990         
7991         
7992         var _this = this;
7993         
7994         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7995             e.on('click', _this.sort, _this);
7996         });
7997         
7998         this.mainBody.on("click", this.onClick, this);
7999         this.mainBody.on("dblclick", this.onDblClick, this);
8000         
8001         // why is this done????? = it breaks dialogs??
8002         //this.parent().el.setStyle('position', 'relative');
8003         
8004         
8005         if (this.footer) {
8006             this.footer.parentId = this.id;
8007             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
8008             
8009             if(this.lazyLoad){
8010                 this.el.select('tfoot tr td').first().addClass('hide');
8011             }
8012         } 
8013         
8014         if(this.loadMask) {
8015             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
8016         }
8017         
8018         this.store.on('load', this.onLoad, this);
8019         this.store.on('beforeload', this.onBeforeLoad, this);
8020         this.store.on('update', this.onUpdate, this);
8021         this.store.on('add', this.onAdd, this);
8022         this.store.on("clear", this.clear, this);
8023         
8024         this.el.on("contextmenu", this.onContextMenu, this);
8025         
8026         this.mainBody.on('scroll', this.onBodyScroll, this);
8027         
8028         this.cm.on("headerchange", this.onHeaderChange, this);
8029         
8030         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8031         
8032     },
8033     
8034     onContextMenu : function(e, t)
8035     {
8036         this.processEvent("contextmenu", e);
8037     },
8038     
8039     processEvent : function(name, e)
8040     {
8041         if (name != 'touchstart' ) {
8042             this.fireEvent(name, e);    
8043         }
8044         
8045         var t = e.getTarget();
8046         
8047         var cell = Roo.get(t);
8048         
8049         if(!cell){
8050             return;
8051         }
8052         
8053         if(cell.findParent('tfoot', false, true)){
8054             return;
8055         }
8056         
8057         if(cell.findParent('thead', false, true)){
8058             
8059             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8060                 cell = Roo.get(t).findParent('th', false, true);
8061                 if (!cell) {
8062                     Roo.log("failed to find th in thead?");
8063                     Roo.log(e.getTarget());
8064                     return;
8065                 }
8066             }
8067             
8068             var cellIndex = cell.dom.cellIndex;
8069             
8070             var ename = name == 'touchstart' ? 'click' : name;
8071             this.fireEvent("header" + ename, this, cellIndex, e);
8072             
8073             return;
8074         }
8075         
8076         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8077             cell = Roo.get(t).findParent('td', false, true);
8078             if (!cell) {
8079                 Roo.log("failed to find th in tbody?");
8080                 Roo.log(e.getTarget());
8081                 return;
8082             }
8083         }
8084         
8085         var row = cell.findParent('tr', false, true);
8086         var cellIndex = cell.dom.cellIndex;
8087         var rowIndex = row.dom.rowIndex - 1;
8088         
8089         if(row !== false){
8090             
8091             this.fireEvent("row" + name, this, rowIndex, e);
8092             
8093             if(cell !== false){
8094             
8095                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8096             }
8097         }
8098         
8099     },
8100     
8101     onMouseover : function(e, el)
8102     {
8103         var cell = Roo.get(el);
8104         
8105         if(!cell){
8106             return;
8107         }
8108         
8109         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8110             cell = cell.findParent('td', false, true);
8111         }
8112         
8113         var row = cell.findParent('tr', false, true);
8114         var cellIndex = cell.dom.cellIndex;
8115         var rowIndex = row.dom.rowIndex - 1; // start from 0
8116         
8117         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8118         
8119     },
8120     
8121     onMouseout : function(e, el)
8122     {
8123         var cell = Roo.get(el);
8124         
8125         if(!cell){
8126             return;
8127         }
8128         
8129         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8130             cell = cell.findParent('td', false, true);
8131         }
8132         
8133         var row = cell.findParent('tr', false, true);
8134         var cellIndex = cell.dom.cellIndex;
8135         var rowIndex = row.dom.rowIndex - 1; // start from 0
8136         
8137         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8138         
8139     },
8140     
8141     onClick : function(e, el)
8142     {
8143         var cell = Roo.get(el);
8144         
8145         if(!cell || (!this.cellSelection && !this.rowSelection)){
8146             return;
8147         }
8148         
8149         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8150             cell = cell.findParent('td', false, true);
8151         }
8152         
8153         if(!cell || typeof(cell) == 'undefined'){
8154             return;
8155         }
8156         
8157         var row = cell.findParent('tr', false, true);
8158         
8159         if(!row || typeof(row) == 'undefined'){
8160             return;
8161         }
8162         
8163         var cellIndex = cell.dom.cellIndex;
8164         var rowIndex = this.getRowIndex(row);
8165         
8166         // why??? - should these not be based on SelectionModel?
8167         if(this.cellSelection){
8168             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8169         }
8170         
8171         if(this.rowSelection){
8172             this.fireEvent('rowclick', this, row, rowIndex, e);
8173         }
8174         
8175         
8176     },
8177         
8178     onDblClick : function(e,el)
8179     {
8180         var cell = Roo.get(el);
8181         
8182         if(!cell || (!this.cellSelection && !this.rowSelection)){
8183             return;
8184         }
8185         
8186         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8187             cell = cell.findParent('td', false, true);
8188         }
8189         
8190         if(!cell || typeof(cell) == 'undefined'){
8191             return;
8192         }
8193         
8194         var row = cell.findParent('tr', false, true);
8195         
8196         if(!row || typeof(row) == 'undefined'){
8197             return;
8198         }
8199         
8200         var cellIndex = cell.dom.cellIndex;
8201         var rowIndex = this.getRowIndex(row);
8202         
8203         if(this.cellSelection){
8204             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8205         }
8206         
8207         if(this.rowSelection){
8208             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8209         }
8210     },
8211     
8212     sort : function(e,el)
8213     {
8214         var col = Roo.get(el);
8215         
8216         if(!col.hasClass('sortable')){
8217             return;
8218         }
8219         
8220         var sort = col.attr('sort');
8221         var dir = 'ASC';
8222         
8223         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8224             dir = 'DESC';
8225         }
8226         
8227         this.store.sortInfo = {field : sort, direction : dir};
8228         
8229         if (this.footer) {
8230             Roo.log("calling footer first");
8231             this.footer.onClick('first');
8232         } else {
8233         
8234             this.store.load({ params : { start : 0 } });
8235         }
8236     },
8237     
8238     renderHeader : function()
8239     {
8240         var header = {
8241             tag: 'thead',
8242             cn : []
8243         };
8244         
8245         var cm = this.cm;
8246         this.totalWidth = 0;
8247         
8248         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8249             
8250             var config = cm.config[i];
8251             
8252             var c = {
8253                 tag: 'th',
8254                 cls : 'x-hcol-' + i,
8255                 style : '',
8256                 html: cm.getColumnHeader(i)
8257             };
8258             
8259             var hh = '';
8260             
8261             if(typeof(config.sortable) != 'undefined' && config.sortable){
8262                 c.cls = 'sortable';
8263                 c.html = '<i class="glyphicon"></i>' + c.html;
8264             }
8265             
8266             // could use BS4 hidden-..-down 
8267             
8268             if(typeof(config.lgHeader) != 'undefined'){
8269                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8270             }
8271             
8272             if(typeof(config.mdHeader) != 'undefined'){
8273                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8274             }
8275             
8276             if(typeof(config.smHeader) != 'undefined'){
8277                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8278             }
8279             
8280             if(typeof(config.xsHeader) != 'undefined'){
8281                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8282             }
8283             
8284             if(hh.length){
8285                 c.html = hh;
8286             }
8287             
8288             if(typeof(config.tooltip) != 'undefined'){
8289                 c.tooltip = config.tooltip;
8290             }
8291             
8292             if(typeof(config.colspan) != 'undefined'){
8293                 c.colspan = config.colspan;
8294             }
8295             
8296             if(typeof(config.hidden) != 'undefined' && config.hidden){
8297                 c.style += ' display:none;';
8298             }
8299             
8300             if(typeof(config.dataIndex) != 'undefined'){
8301                 c.sort = config.dataIndex;
8302             }
8303             
8304            
8305             
8306             if(typeof(config.align) != 'undefined' && config.align.length){
8307                 c.style += ' text-align:' + config.align + ';';
8308             }
8309             
8310             if(typeof(config.width) != 'undefined'){
8311                 c.style += ' width:' + config.width + 'px;';
8312                 this.totalWidth += config.width;
8313             } else {
8314                 this.totalWidth += 100; // assume minimum of 100 per column?
8315             }
8316             
8317             if(typeof(config.cls) != 'undefined'){
8318                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8319             }
8320             
8321             ['xs','sm','md','lg'].map(function(size){
8322                 
8323                 if(typeof(config[size]) == 'undefined'){
8324                     return;
8325                 }
8326                  
8327                 if (!config[size]) { // 0 = hidden
8328                     // BS 4 '0' is treated as hide that column and below.
8329                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8330                     return;
8331                 }
8332                 
8333                 c.cls += ' col-' + size + '-' + config[size] + (
8334                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8335                 );
8336                 
8337                 
8338             });
8339             
8340             header.cn.push(c)
8341         }
8342         
8343         return header;
8344     },
8345     
8346     renderBody : function()
8347     {
8348         var body = {
8349             tag: 'tbody',
8350             cn : [
8351                 {
8352                     tag: 'tr',
8353                     cn : [
8354                         {
8355                             tag : 'td',
8356                             colspan :  this.cm.getColumnCount()
8357                         }
8358                     ]
8359                 }
8360             ]
8361         };
8362         
8363         return body;
8364     },
8365     
8366     renderFooter : function()
8367     {
8368         var footer = {
8369             tag: 'tfoot',
8370             cn : [
8371                 {
8372                     tag: 'tr',
8373                     cn : [
8374                         {
8375                             tag : 'td',
8376                             colspan :  this.cm.getColumnCount()
8377                         }
8378                     ]
8379                 }
8380             ]
8381         };
8382         
8383         return footer;
8384     },
8385     
8386     
8387     
8388     onLoad : function()
8389     {
8390 //        Roo.log('ds onload');
8391         this.clear();
8392         
8393         var _this = this;
8394         var cm = this.cm;
8395         var ds = this.store;
8396         
8397         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8398             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8399             if (_this.store.sortInfo) {
8400                     
8401                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8402                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8403                 }
8404                 
8405                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8406                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8407                 }
8408             }
8409         });
8410         
8411         var tbody =  this.mainBody;
8412               
8413         if(ds.getCount() > 0){
8414             ds.data.each(function(d,rowIndex){
8415                 var row =  this.renderRow(cm, ds, rowIndex);
8416                 
8417                 tbody.createChild(row);
8418                 
8419                 var _this = this;
8420                 
8421                 if(row.cellObjects.length){
8422                     Roo.each(row.cellObjects, function(r){
8423                         _this.renderCellObject(r);
8424                     })
8425                 }
8426                 
8427             }, this);
8428         }
8429         
8430         var tfoot = this.el.select('tfoot', true).first();
8431         
8432         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8433             
8434             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8435             
8436             var total = this.ds.getTotalCount();
8437             
8438             if(this.footer.pageSize < total){
8439                 this.mainFoot.show();
8440             }
8441         }
8442         
8443         Roo.each(this.el.select('tbody td', true).elements, function(e){
8444             e.on('mouseover', _this.onMouseover, _this);
8445         });
8446         
8447         Roo.each(this.el.select('tbody td', true).elements, function(e){
8448             e.on('mouseout', _this.onMouseout, _this);
8449         });
8450         this.fireEvent('rowsrendered', this);
8451         
8452         this.autoSize();
8453     },
8454     
8455     
8456     onUpdate : function(ds,record)
8457     {
8458         this.refreshRow(record);
8459         this.autoSize();
8460     },
8461     
8462     onRemove : function(ds, record, index, isUpdate){
8463         if(isUpdate !== true){
8464             this.fireEvent("beforerowremoved", this, index, record);
8465         }
8466         var bt = this.mainBody.dom;
8467         
8468         var rows = this.el.select('tbody > tr', true).elements;
8469         
8470         if(typeof(rows[index]) != 'undefined'){
8471             bt.removeChild(rows[index].dom);
8472         }
8473         
8474 //        if(bt.rows[index]){
8475 //            bt.removeChild(bt.rows[index]);
8476 //        }
8477         
8478         if(isUpdate !== true){
8479             //this.stripeRows(index);
8480             //this.syncRowHeights(index, index);
8481             //this.layout();
8482             this.fireEvent("rowremoved", this, index, record);
8483         }
8484     },
8485     
8486     onAdd : function(ds, records, rowIndex)
8487     {
8488         //Roo.log('on Add called');
8489         // - note this does not handle multiple adding very well..
8490         var bt = this.mainBody.dom;
8491         for (var i =0 ; i < records.length;i++) {
8492             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8493             //Roo.log(records[i]);
8494             //Roo.log(this.store.getAt(rowIndex+i));
8495             this.insertRow(this.store, rowIndex + i, false);
8496             return;
8497         }
8498         
8499     },
8500     
8501     
8502     refreshRow : function(record){
8503         var ds = this.store, index;
8504         if(typeof record == 'number'){
8505             index = record;
8506             record = ds.getAt(index);
8507         }else{
8508             index = ds.indexOf(record);
8509             if (index < 0) {
8510                 return; // should not happen - but seems to 
8511             }
8512         }
8513         this.insertRow(ds, index, true);
8514         this.autoSize();
8515         this.onRemove(ds, record, index+1, true);
8516         this.autoSize();
8517         //this.syncRowHeights(index, index);
8518         //this.layout();
8519         this.fireEvent("rowupdated", this, index, record);
8520     },
8521     
8522     insertRow : function(dm, rowIndex, isUpdate){
8523         
8524         if(!isUpdate){
8525             this.fireEvent("beforerowsinserted", this, rowIndex);
8526         }
8527             //var s = this.getScrollState();
8528         var row = this.renderRow(this.cm, this.store, rowIndex);
8529         // insert before rowIndex..
8530         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8531         
8532         var _this = this;
8533                 
8534         if(row.cellObjects.length){
8535             Roo.each(row.cellObjects, function(r){
8536                 _this.renderCellObject(r);
8537             })
8538         }
8539             
8540         if(!isUpdate){
8541             this.fireEvent("rowsinserted", this, rowIndex);
8542             //this.syncRowHeights(firstRow, lastRow);
8543             //this.stripeRows(firstRow);
8544             //this.layout();
8545         }
8546         
8547     },
8548     
8549     
8550     getRowDom : function(rowIndex)
8551     {
8552         var rows = this.el.select('tbody > tr', true).elements;
8553         
8554         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8555         
8556     },
8557     // returns the object tree for a tr..
8558   
8559     
8560     renderRow : function(cm, ds, rowIndex) 
8561     {
8562         var d = ds.getAt(rowIndex);
8563         
8564         var row = {
8565             tag : 'tr',
8566             cls : 'x-row-' + rowIndex,
8567             cn : []
8568         };
8569             
8570         var cellObjects = [];
8571         
8572         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8573             var config = cm.config[i];
8574             
8575             var renderer = cm.getRenderer(i);
8576             var value = '';
8577             var id = false;
8578             
8579             if(typeof(renderer) !== 'undefined'){
8580                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8581             }
8582             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8583             // and are rendered into the cells after the row is rendered - using the id for the element.
8584             
8585             if(typeof(value) === 'object'){
8586                 id = Roo.id();
8587                 cellObjects.push({
8588                     container : id,
8589                     cfg : value 
8590                 })
8591             }
8592             
8593             var rowcfg = {
8594                 record: d,
8595                 rowIndex : rowIndex,
8596                 colIndex : i,
8597                 rowClass : ''
8598             };
8599
8600             this.fireEvent('rowclass', this, rowcfg);
8601             
8602             var td = {
8603                 tag: 'td',
8604                 cls : rowcfg.rowClass + ' x-col-' + i,
8605                 style: '',
8606                 html: (typeof(value) === 'object') ? '' : value
8607             };
8608             
8609             if (id) {
8610                 td.id = id;
8611             }
8612             
8613             if(typeof(config.colspan) != 'undefined'){
8614                 td.colspan = config.colspan;
8615             }
8616             
8617             if(typeof(config.hidden) != 'undefined' && config.hidden){
8618                 td.style += ' display:none;';
8619             }
8620             
8621             if(typeof(config.align) != 'undefined' && config.align.length){
8622                 td.style += ' text-align:' + config.align + ';';
8623             }
8624             if(typeof(config.valign) != 'undefined' && config.valign.length){
8625                 td.style += ' vertical-align:' + config.valign + ';';
8626             }
8627             
8628             if(typeof(config.width) != 'undefined'){
8629                 td.style += ' width:' +  config.width + 'px;';
8630             }
8631             
8632             if(typeof(config.cursor) != 'undefined'){
8633                 td.style += ' cursor:' +  config.cursor + ';';
8634             }
8635             
8636             if(typeof(config.cls) != 'undefined'){
8637                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8638             }
8639             
8640             ['xs','sm','md','lg'].map(function(size){
8641                 
8642                 if(typeof(config[size]) == 'undefined'){
8643                     return;
8644                 }
8645                 
8646                 
8647                   
8648                 if (!config[size]) { // 0 = hidden
8649                     // BS 4 '0' is treated as hide that column and below.
8650                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8651                     return;
8652                 }
8653                 
8654                 td.cls += ' col-' + size + '-' + config[size] + (
8655                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8656                 );
8657                  
8658
8659             });
8660             
8661             row.cn.push(td);
8662            
8663         }
8664         
8665         row.cellObjects = cellObjects;
8666         
8667         return row;
8668           
8669     },
8670     
8671     
8672     
8673     onBeforeLoad : function()
8674     {
8675         
8676     },
8677      /**
8678      * Remove all rows
8679      */
8680     clear : function()
8681     {
8682         this.el.select('tbody', true).first().dom.innerHTML = '';
8683     },
8684     /**
8685      * Show or hide a row.
8686      * @param {Number} rowIndex to show or hide
8687      * @param {Boolean} state hide
8688      */
8689     setRowVisibility : function(rowIndex, state)
8690     {
8691         var bt = this.mainBody.dom;
8692         
8693         var rows = this.el.select('tbody > tr', true).elements;
8694         
8695         if(typeof(rows[rowIndex]) == 'undefined'){
8696             return;
8697         }
8698         rows[rowIndex].dom.style.display = state ? '' : 'none';
8699     },
8700     
8701     
8702     getSelectionModel : function(){
8703         if(!this.selModel){
8704             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8705         }
8706         return this.selModel;
8707     },
8708     /*
8709      * Render the Roo.bootstrap object from renderder
8710      */
8711     renderCellObject : function(r)
8712     {
8713         var _this = this;
8714         
8715         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8716         
8717         var t = r.cfg.render(r.container);
8718         
8719         if(r.cfg.cn){
8720             Roo.each(r.cfg.cn, function(c){
8721                 var child = {
8722                     container: t.getChildContainer(),
8723                     cfg: c
8724                 };
8725                 _this.renderCellObject(child);
8726             })
8727         }
8728     },
8729     
8730     getRowIndex : function(row)
8731     {
8732         var rowIndex = -1;
8733         
8734         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8735             if(el != row){
8736                 return;
8737             }
8738             
8739             rowIndex = index;
8740         });
8741         
8742         return rowIndex;
8743     },
8744      /**
8745      * Returns the grid's underlying element = used by panel.Grid
8746      * @return {Element} The element
8747      */
8748     getGridEl : function(){
8749         return this.el;
8750     },
8751      /**
8752      * Forces a resize - used by panel.Grid
8753      * @return {Element} The element
8754      */
8755     autoSize : function()
8756     {
8757         //var ctr = Roo.get(this.container.dom.parentElement);
8758         var ctr = Roo.get(this.el.dom);
8759         
8760         var thd = this.getGridEl().select('thead',true).first();
8761         var tbd = this.getGridEl().select('tbody', true).first();
8762         var tfd = this.getGridEl().select('tfoot', true).first();
8763         
8764         var cw = ctr.getWidth();
8765         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8766         
8767         if (tbd) {
8768             
8769             tbd.setWidth(ctr.getWidth());
8770             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8771             // this needs fixing for various usage - currently only hydra job advers I think..
8772             //tdb.setHeight(
8773             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8774             //); 
8775             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8776             cw -= barsize;
8777         }
8778         cw = Math.max(cw, this.totalWidth);
8779         this.getGridEl().select('tbody tr',true).setWidth(cw);
8780         
8781         // resize 'expandable coloumn?
8782         
8783         return; // we doe not have a view in this design..
8784         
8785     },
8786     onBodyScroll: function()
8787     {
8788         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8789         if(this.mainHead){
8790             this.mainHead.setStyle({
8791                 'position' : 'relative',
8792                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8793             });
8794         }
8795         
8796         if(this.lazyLoad){
8797             
8798             var scrollHeight = this.mainBody.dom.scrollHeight;
8799             
8800             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8801             
8802             var height = this.mainBody.getHeight();
8803             
8804             if(scrollHeight - height == scrollTop) {
8805                 
8806                 var total = this.ds.getTotalCount();
8807                 
8808                 if(this.footer.cursor + this.footer.pageSize < total){
8809                     
8810                     this.footer.ds.load({
8811                         params : {
8812                             start : this.footer.cursor + this.footer.pageSize,
8813                             limit : this.footer.pageSize
8814                         },
8815                         add : true
8816                     });
8817                 }
8818             }
8819             
8820         }
8821     },
8822     
8823     onHeaderChange : function()
8824     {
8825         var header = this.renderHeader();
8826         var table = this.el.select('table', true).first();
8827         
8828         this.mainHead.remove();
8829         this.mainHead = table.createChild(header, this.mainBody, false);
8830     },
8831     
8832     onHiddenChange : function(colModel, colIndex, hidden)
8833     {
8834         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8835         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8836         
8837         this.CSS.updateRule(thSelector, "display", "");
8838         this.CSS.updateRule(tdSelector, "display", "");
8839         
8840         if(hidden){
8841             this.CSS.updateRule(thSelector, "display", "none");
8842             this.CSS.updateRule(tdSelector, "display", "none");
8843         }
8844         
8845         this.onHeaderChange();
8846         this.onLoad();
8847     },
8848     
8849     setColumnWidth: function(col_index, width)
8850     {
8851         // width = "md-2 xs-2..."
8852         if(!this.colModel.config[col_index]) {
8853             return;
8854         }
8855         
8856         var w = width.split(" ");
8857         
8858         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8859         
8860         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8861         
8862         
8863         for(var j = 0; j < w.length; j++) {
8864             
8865             if(!w[j]) {
8866                 continue;
8867             }
8868             
8869             var size_cls = w[j].split("-");
8870             
8871             if(!Number.isInteger(size_cls[1] * 1)) {
8872                 continue;
8873             }
8874             
8875             if(!this.colModel.config[col_index][size_cls[0]]) {
8876                 continue;
8877             }
8878             
8879             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8880                 continue;
8881             }
8882             
8883             h_row[0].classList.replace(
8884                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8885                 "col-"+size_cls[0]+"-"+size_cls[1]
8886             );
8887             
8888             for(var i = 0; i < rows.length; i++) {
8889                 
8890                 var size_cls = w[j].split("-");
8891                 
8892                 if(!Number.isInteger(size_cls[1] * 1)) {
8893                     continue;
8894                 }
8895                 
8896                 if(!this.colModel.config[col_index][size_cls[0]]) {
8897                     continue;
8898                 }
8899                 
8900                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8901                     continue;
8902                 }
8903                 
8904                 rows[i].classList.replace(
8905                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8906                     "col-"+size_cls[0]+"-"+size_cls[1]
8907                 );
8908             }
8909             
8910             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8911         }
8912     }
8913 });
8914
8915  
8916
8917  /*
8918  * - LGPL
8919  *
8920  * table cell
8921  * 
8922  */
8923
8924 /**
8925  * @class Roo.bootstrap.TableCell
8926  * @extends Roo.bootstrap.Component
8927  * Bootstrap TableCell class
8928  * @cfg {String} html cell contain text
8929  * @cfg {String} cls cell class
8930  * @cfg {String} tag cell tag (td|th) default td
8931  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8932  * @cfg {String} align Aligns the content in a cell
8933  * @cfg {String} axis Categorizes cells
8934  * @cfg {String} bgcolor Specifies the background color of a cell
8935  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8936  * @cfg {Number} colspan Specifies the number of columns a cell should span
8937  * @cfg {String} headers Specifies one or more header cells a cell is related to
8938  * @cfg {Number} height Sets the height of a cell
8939  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8940  * @cfg {Number} rowspan Sets the number of rows a cell should span
8941  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8942  * @cfg {String} valign Vertical aligns the content in a cell
8943  * @cfg {Number} width Specifies the width of a cell
8944  * 
8945  * @constructor
8946  * Create a new TableCell
8947  * @param {Object} config The config object
8948  */
8949
8950 Roo.bootstrap.TableCell = function(config){
8951     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8952 };
8953
8954 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8955     
8956     html: false,
8957     cls: false,
8958     tag: false,
8959     abbr: false,
8960     align: false,
8961     axis: false,
8962     bgcolor: false,
8963     charoff: false,
8964     colspan: false,
8965     headers: false,
8966     height: false,
8967     nowrap: false,
8968     rowspan: false,
8969     scope: false,
8970     valign: false,
8971     width: false,
8972     
8973     
8974     getAutoCreate : function(){
8975         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8976         
8977         cfg = {
8978             tag: 'td'
8979         };
8980         
8981         if(this.tag){
8982             cfg.tag = this.tag;
8983         }
8984         
8985         if (this.html) {
8986             cfg.html=this.html
8987         }
8988         if (this.cls) {
8989             cfg.cls=this.cls
8990         }
8991         if (this.abbr) {
8992             cfg.abbr=this.abbr
8993         }
8994         if (this.align) {
8995             cfg.align=this.align
8996         }
8997         if (this.axis) {
8998             cfg.axis=this.axis
8999         }
9000         if (this.bgcolor) {
9001             cfg.bgcolor=this.bgcolor
9002         }
9003         if (this.charoff) {
9004             cfg.charoff=this.charoff
9005         }
9006         if (this.colspan) {
9007             cfg.colspan=this.colspan
9008         }
9009         if (this.headers) {
9010             cfg.headers=this.headers
9011         }
9012         if (this.height) {
9013             cfg.height=this.height
9014         }
9015         if (this.nowrap) {
9016             cfg.nowrap=this.nowrap
9017         }
9018         if (this.rowspan) {
9019             cfg.rowspan=this.rowspan
9020         }
9021         if (this.scope) {
9022             cfg.scope=this.scope
9023         }
9024         if (this.valign) {
9025             cfg.valign=this.valign
9026         }
9027         if (this.width) {
9028             cfg.width=this.width
9029         }
9030         
9031         
9032         return cfg;
9033     }
9034    
9035 });
9036
9037  
9038
9039  /*
9040  * - LGPL
9041  *
9042  * table row
9043  * 
9044  */
9045
9046 /**
9047  * @class Roo.bootstrap.TableRow
9048  * @extends Roo.bootstrap.Component
9049  * Bootstrap TableRow class
9050  * @cfg {String} cls row class
9051  * @cfg {String} align Aligns the content in a table row
9052  * @cfg {String} bgcolor Specifies a background color for a table row
9053  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9054  * @cfg {String} valign Vertical aligns the content in a table row
9055  * 
9056  * @constructor
9057  * Create a new TableRow
9058  * @param {Object} config The config object
9059  */
9060
9061 Roo.bootstrap.TableRow = function(config){
9062     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9063 };
9064
9065 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9066     
9067     cls: false,
9068     align: false,
9069     bgcolor: false,
9070     charoff: false,
9071     valign: false,
9072     
9073     getAutoCreate : function(){
9074         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9075         
9076         cfg = {
9077             tag: 'tr'
9078         };
9079             
9080         if(this.cls){
9081             cfg.cls = this.cls;
9082         }
9083         if(this.align){
9084             cfg.align = this.align;
9085         }
9086         if(this.bgcolor){
9087             cfg.bgcolor = this.bgcolor;
9088         }
9089         if(this.charoff){
9090             cfg.charoff = this.charoff;
9091         }
9092         if(this.valign){
9093             cfg.valign = this.valign;
9094         }
9095         
9096         return cfg;
9097     }
9098    
9099 });
9100
9101  
9102
9103  /*
9104  * - LGPL
9105  *
9106  * table body
9107  * 
9108  */
9109
9110 /**
9111  * @class Roo.bootstrap.TableBody
9112  * @extends Roo.bootstrap.Component
9113  * Bootstrap TableBody class
9114  * @cfg {String} cls element class
9115  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9116  * @cfg {String} align Aligns the content inside the element
9117  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9118  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9119  * 
9120  * @constructor
9121  * Create a new TableBody
9122  * @param {Object} config The config object
9123  */
9124
9125 Roo.bootstrap.TableBody = function(config){
9126     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9127 };
9128
9129 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9130     
9131     cls: false,
9132     tag: false,
9133     align: false,
9134     charoff: false,
9135     valign: false,
9136     
9137     getAutoCreate : function(){
9138         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9139         
9140         cfg = {
9141             tag: 'tbody'
9142         };
9143             
9144         if (this.cls) {
9145             cfg.cls=this.cls
9146         }
9147         if(this.tag){
9148             cfg.tag = this.tag;
9149         }
9150         
9151         if(this.align){
9152             cfg.align = this.align;
9153         }
9154         if(this.charoff){
9155             cfg.charoff = this.charoff;
9156         }
9157         if(this.valign){
9158             cfg.valign = this.valign;
9159         }
9160         
9161         return cfg;
9162     }
9163     
9164     
9165 //    initEvents : function()
9166 //    {
9167 //        
9168 //        if(!this.store){
9169 //            return;
9170 //        }
9171 //        
9172 //        this.store = Roo.factory(this.store, Roo.data);
9173 //        this.store.on('load', this.onLoad, this);
9174 //        
9175 //        this.store.load();
9176 //        
9177 //    },
9178 //    
9179 //    onLoad: function () 
9180 //    {   
9181 //        this.fireEvent('load', this);
9182 //    }
9183 //    
9184 //   
9185 });
9186
9187  
9188
9189  /*
9190  * Based on:
9191  * Ext JS Library 1.1.1
9192  * Copyright(c) 2006-2007, Ext JS, LLC.
9193  *
9194  * Originally Released Under LGPL - original licence link has changed is not relivant.
9195  *
9196  * Fork - LGPL
9197  * <script type="text/javascript">
9198  */
9199
9200 // as we use this in bootstrap.
9201 Roo.namespace('Roo.form');
9202  /**
9203  * @class Roo.form.Action
9204  * Internal Class used to handle form actions
9205  * @constructor
9206  * @param {Roo.form.BasicForm} el The form element or its id
9207  * @param {Object} config Configuration options
9208  */
9209
9210  
9211  
9212 // define the action interface
9213 Roo.form.Action = function(form, options){
9214     this.form = form;
9215     this.options = options || {};
9216 };
9217 /**
9218  * Client Validation Failed
9219  * @const 
9220  */
9221 Roo.form.Action.CLIENT_INVALID = 'client';
9222 /**
9223  * Server Validation Failed
9224  * @const 
9225  */
9226 Roo.form.Action.SERVER_INVALID = 'server';
9227  /**
9228  * Connect to Server Failed
9229  * @const 
9230  */
9231 Roo.form.Action.CONNECT_FAILURE = 'connect';
9232 /**
9233  * Reading Data from Server Failed
9234  * @const 
9235  */
9236 Roo.form.Action.LOAD_FAILURE = 'load';
9237
9238 Roo.form.Action.prototype = {
9239     type : 'default',
9240     failureType : undefined,
9241     response : undefined,
9242     result : undefined,
9243
9244     // interface method
9245     run : function(options){
9246
9247     },
9248
9249     // interface method
9250     success : function(response){
9251
9252     },
9253
9254     // interface method
9255     handleResponse : function(response){
9256
9257     },
9258
9259     // default connection failure
9260     failure : function(response){
9261         
9262         this.response = response;
9263         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9264         this.form.afterAction(this, false);
9265     },
9266
9267     processResponse : function(response){
9268         this.response = response;
9269         if(!response.responseText){
9270             return true;
9271         }
9272         this.result = this.handleResponse(response);
9273         return this.result;
9274     },
9275
9276     // utility functions used internally
9277     getUrl : function(appendParams){
9278         var url = this.options.url || this.form.url || this.form.el.dom.action;
9279         if(appendParams){
9280             var p = this.getParams();
9281             if(p){
9282                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9283             }
9284         }
9285         return url;
9286     },
9287
9288     getMethod : function(){
9289         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9290     },
9291
9292     getParams : function(){
9293         var bp = this.form.baseParams;
9294         var p = this.options.params;
9295         if(p){
9296             if(typeof p == "object"){
9297                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9298             }else if(typeof p == 'string' && bp){
9299                 p += '&' + Roo.urlEncode(bp);
9300             }
9301         }else if(bp){
9302             p = Roo.urlEncode(bp);
9303         }
9304         return p;
9305     },
9306
9307     createCallback : function(){
9308         return {
9309             success: this.success,
9310             failure: this.failure,
9311             scope: this,
9312             timeout: (this.form.timeout*1000),
9313             upload: this.form.fileUpload ? this.success : undefined
9314         };
9315     }
9316 };
9317
9318 Roo.form.Action.Submit = function(form, options){
9319     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9320 };
9321
9322 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9323     type : 'submit',
9324
9325     haveProgress : false,
9326     uploadComplete : false,
9327     
9328     // uploadProgress indicator.
9329     uploadProgress : function()
9330     {
9331         if (!this.form.progressUrl) {
9332             return;
9333         }
9334         
9335         if (!this.haveProgress) {
9336             Roo.MessageBox.progress("Uploading", "Uploading");
9337         }
9338         if (this.uploadComplete) {
9339            Roo.MessageBox.hide();
9340            return;
9341         }
9342         
9343         this.haveProgress = true;
9344    
9345         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9346         
9347         var c = new Roo.data.Connection();
9348         c.request({
9349             url : this.form.progressUrl,
9350             params: {
9351                 id : uid
9352             },
9353             method: 'GET',
9354             success : function(req){
9355                //console.log(data);
9356                 var rdata = false;
9357                 var edata;
9358                 try  {
9359                    rdata = Roo.decode(req.responseText)
9360                 } catch (e) {
9361                     Roo.log("Invalid data from server..");
9362                     Roo.log(edata);
9363                     return;
9364                 }
9365                 if (!rdata || !rdata.success) {
9366                     Roo.log(rdata);
9367                     Roo.MessageBox.alert(Roo.encode(rdata));
9368                     return;
9369                 }
9370                 var data = rdata.data;
9371                 
9372                 if (this.uploadComplete) {
9373                    Roo.MessageBox.hide();
9374                    return;
9375                 }
9376                    
9377                 if (data){
9378                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9379                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9380                     );
9381                 }
9382                 this.uploadProgress.defer(2000,this);
9383             },
9384        
9385             failure: function(data) {
9386                 Roo.log('progress url failed ');
9387                 Roo.log(data);
9388             },
9389             scope : this
9390         });
9391            
9392     },
9393     
9394     
9395     run : function()
9396     {
9397         // run get Values on the form, so it syncs any secondary forms.
9398         this.form.getValues();
9399         
9400         var o = this.options;
9401         var method = this.getMethod();
9402         var isPost = method == 'POST';
9403         if(o.clientValidation === false || this.form.isValid()){
9404             
9405             if (this.form.progressUrl) {
9406                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9407                     (new Date() * 1) + '' + Math.random());
9408                     
9409             } 
9410             
9411             
9412             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9413                 form:this.form.el.dom,
9414                 url:this.getUrl(!isPost),
9415                 method: method,
9416                 params:isPost ? this.getParams() : null,
9417                 isUpload: this.form.fileUpload,
9418                 formData : this.form.formData
9419             }));
9420             
9421             this.uploadProgress();
9422
9423         }else if (o.clientValidation !== false){ // client validation failed
9424             this.failureType = Roo.form.Action.CLIENT_INVALID;
9425             this.form.afterAction(this, false);
9426         }
9427     },
9428
9429     success : function(response)
9430     {
9431         this.uploadComplete= true;
9432         if (this.haveProgress) {
9433             Roo.MessageBox.hide();
9434         }
9435         
9436         
9437         var result = this.processResponse(response);
9438         if(result === true || result.success){
9439             this.form.afterAction(this, true);
9440             return;
9441         }
9442         if(result.errors){
9443             this.form.markInvalid(result.errors);
9444             this.failureType = Roo.form.Action.SERVER_INVALID;
9445         }
9446         this.form.afterAction(this, false);
9447     },
9448     failure : function(response)
9449     {
9450         this.uploadComplete= true;
9451         if (this.haveProgress) {
9452             Roo.MessageBox.hide();
9453         }
9454         
9455         this.response = response;
9456         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9457         this.form.afterAction(this, false);
9458     },
9459     
9460     handleResponse : function(response){
9461         if(this.form.errorReader){
9462             var rs = this.form.errorReader.read(response);
9463             var errors = [];
9464             if(rs.records){
9465                 for(var i = 0, len = rs.records.length; i < len; i++) {
9466                     var r = rs.records[i];
9467                     errors[i] = r.data;
9468                 }
9469             }
9470             if(errors.length < 1){
9471                 errors = null;
9472             }
9473             return {
9474                 success : rs.success,
9475                 errors : errors
9476             };
9477         }
9478         var ret = false;
9479         try {
9480             ret = Roo.decode(response.responseText);
9481         } catch (e) {
9482             ret = {
9483                 success: false,
9484                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9485                 errors : []
9486             };
9487         }
9488         return ret;
9489         
9490     }
9491 });
9492
9493
9494 Roo.form.Action.Load = function(form, options){
9495     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9496     this.reader = this.form.reader;
9497 };
9498
9499 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9500     type : 'load',
9501
9502     run : function(){
9503         
9504         Roo.Ajax.request(Roo.apply(
9505                 this.createCallback(), {
9506                     method:this.getMethod(),
9507                     url:this.getUrl(false),
9508                     params:this.getParams()
9509         }));
9510     },
9511
9512     success : function(response){
9513         
9514         var result = this.processResponse(response);
9515         if(result === true || !result.success || !result.data){
9516             this.failureType = Roo.form.Action.LOAD_FAILURE;
9517             this.form.afterAction(this, false);
9518             return;
9519         }
9520         this.form.clearInvalid();
9521         this.form.setValues(result.data);
9522         this.form.afterAction(this, true);
9523     },
9524
9525     handleResponse : function(response){
9526         if(this.form.reader){
9527             var rs = this.form.reader.read(response);
9528             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9529             return {
9530                 success : rs.success,
9531                 data : data
9532             };
9533         }
9534         return Roo.decode(response.responseText);
9535     }
9536 });
9537
9538 Roo.form.Action.ACTION_TYPES = {
9539     'load' : Roo.form.Action.Load,
9540     'submit' : Roo.form.Action.Submit
9541 };/*
9542  * - LGPL
9543  *
9544  * form
9545  *
9546  */
9547
9548 /**
9549  * @class Roo.bootstrap.Form
9550  * @extends Roo.bootstrap.Component
9551  * Bootstrap Form class
9552  * @cfg {String} method  GET | POST (default POST)
9553  * @cfg {String} labelAlign top | left (default top)
9554  * @cfg {String} align left  | right - for navbars
9555  * @cfg {Boolean} loadMask load mask when submit (default true)
9556
9557  *
9558  * @constructor
9559  * Create a new Form
9560  * @param {Object} config The config object
9561  */
9562
9563
9564 Roo.bootstrap.Form = function(config){
9565     
9566     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9567     
9568     Roo.bootstrap.Form.popover.apply();
9569     
9570     this.addEvents({
9571         /**
9572          * @event clientvalidation
9573          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9574          * @param {Form} this
9575          * @param {Boolean} valid true if the form has passed client-side validation
9576          */
9577         clientvalidation: true,
9578         /**
9579          * @event beforeaction
9580          * Fires before any action is performed. Return false to cancel the action.
9581          * @param {Form} this
9582          * @param {Action} action The action to be performed
9583          */
9584         beforeaction: true,
9585         /**
9586          * @event actionfailed
9587          * Fires when an action fails.
9588          * @param {Form} this
9589          * @param {Action} action The action that failed
9590          */
9591         actionfailed : true,
9592         /**
9593          * @event actioncomplete
9594          * Fires when an action is completed.
9595          * @param {Form} this
9596          * @param {Action} action The action that completed
9597          */
9598         actioncomplete : true
9599     });
9600 };
9601
9602 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9603
9604      /**
9605      * @cfg {String} method
9606      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9607      */
9608     method : 'POST',
9609     /**
9610      * @cfg {String} url
9611      * The URL to use for form actions if one isn't supplied in the action options.
9612      */
9613     /**
9614      * @cfg {Boolean} fileUpload
9615      * Set to true if this form is a file upload.
9616      */
9617
9618     /**
9619      * @cfg {Object} baseParams
9620      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9621      */
9622
9623     /**
9624      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9625      */
9626     timeout: 30,
9627     /**
9628      * @cfg {Sting} align (left|right) for navbar forms
9629      */
9630     align : 'left',
9631
9632     // private
9633     activeAction : null,
9634
9635     /**
9636      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9637      * element by passing it or its id or mask the form itself by passing in true.
9638      * @type Mixed
9639      */
9640     waitMsgTarget : false,
9641
9642     loadMask : true,
9643     
9644     /**
9645      * @cfg {Boolean} errorMask (true|false) default false
9646      */
9647     errorMask : false,
9648     
9649     /**
9650      * @cfg {Number} maskOffset Default 100
9651      */
9652     maskOffset : 100,
9653     
9654     /**
9655      * @cfg {Boolean} maskBody
9656      */
9657     maskBody : false,
9658
9659     getAutoCreate : function(){
9660
9661         var cfg = {
9662             tag: 'form',
9663             method : this.method || 'POST',
9664             id : this.id || Roo.id(),
9665             cls : ''
9666         };
9667         if (this.parent().xtype.match(/^Nav/)) {
9668             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9669
9670         }
9671
9672         if (this.labelAlign == 'left' ) {
9673             cfg.cls += ' form-horizontal';
9674         }
9675
9676
9677         return cfg;
9678     },
9679     initEvents : function()
9680     {
9681         this.el.on('submit', this.onSubmit, this);
9682         // this was added as random key presses on the form where triggering form submit.
9683         this.el.on('keypress', function(e) {
9684             if (e.getCharCode() != 13) {
9685                 return true;
9686             }
9687             // we might need to allow it for textareas.. and some other items.
9688             // check e.getTarget().
9689
9690             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9691                 return true;
9692             }
9693
9694             Roo.log("keypress blocked");
9695
9696             e.preventDefault();
9697             return false;
9698         });
9699         
9700     },
9701     // private
9702     onSubmit : function(e){
9703         e.stopEvent();
9704     },
9705
9706      /**
9707      * Returns true if client-side validation on the form is successful.
9708      * @return Boolean
9709      */
9710     isValid : function(){
9711         var items = this.getItems();
9712         var valid = true;
9713         var target = false;
9714         
9715         items.each(function(f){
9716             
9717             if(f.validate()){
9718                 return;
9719             }
9720             
9721             Roo.log('invalid field: ' + f.name);
9722             
9723             valid = false;
9724
9725             if(!target && f.el.isVisible(true)){
9726                 target = f;
9727             }
9728            
9729         });
9730         
9731         if(this.errorMask && !valid){
9732             Roo.bootstrap.Form.popover.mask(this, target);
9733         }
9734         
9735         return valid;
9736     },
9737     
9738     /**
9739      * Returns true if any fields in this form have changed since their original load.
9740      * @return Boolean
9741      */
9742     isDirty : function(){
9743         var dirty = false;
9744         var items = this.getItems();
9745         items.each(function(f){
9746            if(f.isDirty()){
9747                dirty = true;
9748                return false;
9749            }
9750            return true;
9751         });
9752         return dirty;
9753     },
9754      /**
9755      * Performs a predefined action (submit or load) or custom actions you define on this form.
9756      * @param {String} actionName The name of the action type
9757      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9758      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9759      * accept other config options):
9760      * <pre>
9761 Property          Type             Description
9762 ----------------  ---------------  ----------------------------------------------------------------------------------
9763 url               String           The url for the action (defaults to the form's url)
9764 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9765 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9766 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9767                                    validate the form on the client (defaults to false)
9768      * </pre>
9769      * @return {BasicForm} this
9770      */
9771     doAction : function(action, options){
9772         if(typeof action == 'string'){
9773             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9774         }
9775         if(this.fireEvent('beforeaction', this, action) !== false){
9776             this.beforeAction(action);
9777             action.run.defer(100, action);
9778         }
9779         return this;
9780     },
9781
9782     // private
9783     beforeAction : function(action){
9784         var o = action.options;
9785         
9786         if(this.loadMask){
9787             
9788             if(this.maskBody){
9789                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9790             } else {
9791                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9792             }
9793         }
9794         // not really supported yet.. ??
9795
9796         //if(this.waitMsgTarget === true){
9797         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9798         //}else if(this.waitMsgTarget){
9799         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9800         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9801         //}else {
9802         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9803        // }
9804
9805     },
9806
9807     // private
9808     afterAction : function(action, success){
9809         this.activeAction = null;
9810         var o = action.options;
9811
9812         if(this.loadMask){
9813             
9814             if(this.maskBody){
9815                 Roo.get(document.body).unmask();
9816             } else {
9817                 this.el.unmask();
9818             }
9819         }
9820         
9821         //if(this.waitMsgTarget === true){
9822 //            this.el.unmask();
9823         //}else if(this.waitMsgTarget){
9824         //    this.waitMsgTarget.unmask();
9825         //}else{
9826         //    Roo.MessageBox.updateProgress(1);
9827         //    Roo.MessageBox.hide();
9828        // }
9829         //
9830         if(success){
9831             if(o.reset){
9832                 this.reset();
9833             }
9834             Roo.callback(o.success, o.scope, [this, action]);
9835             this.fireEvent('actioncomplete', this, action);
9836
9837         }else{
9838
9839             // failure condition..
9840             // we have a scenario where updates need confirming.
9841             // eg. if a locking scenario exists..
9842             // we look for { errors : { needs_confirm : true }} in the response.
9843             if (
9844                 (typeof(action.result) != 'undefined')  &&
9845                 (typeof(action.result.errors) != 'undefined')  &&
9846                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9847            ){
9848                 var _t = this;
9849                 Roo.log("not supported yet");
9850                  /*
9851
9852                 Roo.MessageBox.confirm(
9853                     "Change requires confirmation",
9854                     action.result.errorMsg,
9855                     function(r) {
9856                         if (r != 'yes') {
9857                             return;
9858                         }
9859                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9860                     }
9861
9862                 );
9863                 */
9864
9865
9866                 return;
9867             }
9868
9869             Roo.callback(o.failure, o.scope, [this, action]);
9870             // show an error message if no failed handler is set..
9871             if (!this.hasListener('actionfailed')) {
9872                 Roo.log("need to add dialog support");
9873                 /*
9874                 Roo.MessageBox.alert("Error",
9875                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9876                         action.result.errorMsg :
9877                         "Saving Failed, please check your entries or try again"
9878                 );
9879                 */
9880             }
9881
9882             this.fireEvent('actionfailed', this, action);
9883         }
9884
9885     },
9886     /**
9887      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9888      * @param {String} id The value to search for
9889      * @return Field
9890      */
9891     findField : function(id){
9892         var items = this.getItems();
9893         var field = items.get(id);
9894         if(!field){
9895              items.each(function(f){
9896                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9897                     field = f;
9898                     return false;
9899                 }
9900                 return true;
9901             });
9902         }
9903         return field || null;
9904     },
9905      /**
9906      * Mark fields in this form invalid in bulk.
9907      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9908      * @return {BasicForm} this
9909      */
9910     markInvalid : function(errors){
9911         if(errors instanceof Array){
9912             for(var i = 0, len = errors.length; i < len; i++){
9913                 var fieldError = errors[i];
9914                 var f = this.findField(fieldError.id);
9915                 if(f){
9916                     f.markInvalid(fieldError.msg);
9917                 }
9918             }
9919         }else{
9920             var field, id;
9921             for(id in errors){
9922                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9923                     field.markInvalid(errors[id]);
9924                 }
9925             }
9926         }
9927         //Roo.each(this.childForms || [], function (f) {
9928         //    f.markInvalid(errors);
9929         //});
9930
9931         return this;
9932     },
9933
9934     /**
9935      * Set values for fields in this form in bulk.
9936      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9937      * @return {BasicForm} this
9938      */
9939     setValues : function(values){
9940         if(values instanceof Array){ // array of objects
9941             for(var i = 0, len = values.length; i < len; i++){
9942                 var v = values[i];
9943                 var f = this.findField(v.id);
9944                 if(f){
9945                     f.setValue(v.value);
9946                     if(this.trackResetOnLoad){
9947                         f.originalValue = f.getValue();
9948                     }
9949                 }
9950             }
9951         }else{ // object hash
9952             var field, id;
9953             for(id in values){
9954                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9955
9956                     if (field.setFromData &&
9957                         field.valueField &&
9958                         field.displayField &&
9959                         // combos' with local stores can
9960                         // be queried via setValue()
9961                         // to set their value..
9962                         (field.store && !field.store.isLocal)
9963                         ) {
9964                         // it's a combo
9965                         var sd = { };
9966                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9967                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9968                         field.setFromData(sd);
9969
9970                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9971                         
9972                         field.setFromData(values);
9973                         
9974                     } else {
9975                         field.setValue(values[id]);
9976                     }
9977
9978
9979                     if(this.trackResetOnLoad){
9980                         field.originalValue = field.getValue();
9981                     }
9982                 }
9983             }
9984         }
9985
9986         //Roo.each(this.childForms || [], function (f) {
9987         //    f.setValues(values);
9988         //});
9989
9990         return this;
9991     },
9992
9993     /**
9994      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9995      * they are returned as an array.
9996      * @param {Boolean} asString
9997      * @return {Object}
9998      */
9999     getValues : function(asString){
10000         //if (this.childForms) {
10001             // copy values from the child forms
10002         //    Roo.each(this.childForms, function (f) {
10003         //        this.setValues(f.getValues());
10004         //    }, this);
10005         //}
10006
10007
10008
10009         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
10010         if(asString === true){
10011             return fs;
10012         }
10013         return Roo.urlDecode(fs);
10014     },
10015
10016     /**
10017      * Returns the fields in this form as an object with key/value pairs.
10018      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
10019      * @return {Object}
10020      */
10021     getFieldValues : function(with_hidden)
10022     {
10023         var items = this.getItems();
10024         var ret = {};
10025         items.each(function(f){
10026             
10027             if (!f.getName()) {
10028                 return;
10029             }
10030             
10031             var v = f.getValue();
10032             
10033             if (f.inputType =='radio') {
10034                 if (typeof(ret[f.getName()]) == 'undefined') {
10035                     ret[f.getName()] = ''; // empty..
10036                 }
10037
10038                 if (!f.el.dom.checked) {
10039                     return;
10040
10041                 }
10042                 v = f.el.dom.value;
10043
10044             }
10045             
10046             if(f.xtype == 'MoneyField'){
10047                 ret[f.currencyName] = f.getCurrency();
10048             }
10049
10050             // not sure if this supported any more..
10051             if ((typeof(v) == 'object') && f.getRawValue) {
10052                 v = f.getRawValue() ; // dates..
10053             }
10054             // combo boxes where name != hiddenName...
10055             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10056                 ret[f.name] = f.getRawValue();
10057             }
10058             ret[f.getName()] = v;
10059         });
10060
10061         return ret;
10062     },
10063
10064     /**
10065      * Clears all invalid messages in this form.
10066      * @return {BasicForm} this
10067      */
10068     clearInvalid : function(){
10069         var items = this.getItems();
10070
10071         items.each(function(f){
10072            f.clearInvalid();
10073         });
10074
10075         return this;
10076     },
10077
10078     /**
10079      * Resets this form.
10080      * @return {BasicForm} this
10081      */
10082     reset : function(){
10083         var items = this.getItems();
10084         items.each(function(f){
10085             f.reset();
10086         });
10087
10088         Roo.each(this.childForms || [], function (f) {
10089             f.reset();
10090         });
10091
10092
10093         return this;
10094     },
10095     
10096     getItems : function()
10097     {
10098         var r=new Roo.util.MixedCollection(false, function(o){
10099             return o.id || (o.id = Roo.id());
10100         });
10101         var iter = function(el) {
10102             if (el.inputEl) {
10103                 r.add(el);
10104             }
10105             if (!el.items) {
10106                 return;
10107             }
10108             Roo.each(el.items,function(e) {
10109                 iter(e);
10110             });
10111         };
10112
10113         iter(this);
10114         return r;
10115     },
10116     
10117     hideFields : function(items)
10118     {
10119         Roo.each(items, function(i){
10120             
10121             var f = this.findField(i);
10122             
10123             if(!f){
10124                 return;
10125             }
10126             
10127             f.hide();
10128             
10129         }, this);
10130     },
10131     
10132     showFields : function(items)
10133     {
10134         Roo.each(items, function(i){
10135             
10136             var f = this.findField(i);
10137             
10138             if(!f){
10139                 return;
10140             }
10141             
10142             f.show();
10143             
10144         }, this);
10145     }
10146
10147 });
10148
10149 Roo.apply(Roo.bootstrap.Form, {
10150     
10151     popover : {
10152         
10153         padding : 5,
10154         
10155         isApplied : false,
10156         
10157         isMasked : false,
10158         
10159         form : false,
10160         
10161         target : false,
10162         
10163         toolTip : false,
10164         
10165         intervalID : false,
10166         
10167         maskEl : false,
10168         
10169         apply : function()
10170         {
10171             if(this.isApplied){
10172                 return;
10173             }
10174             
10175             this.maskEl = {
10176                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10177                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10178                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10179                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10180             };
10181             
10182             this.maskEl.top.enableDisplayMode("block");
10183             this.maskEl.left.enableDisplayMode("block");
10184             this.maskEl.bottom.enableDisplayMode("block");
10185             this.maskEl.right.enableDisplayMode("block");
10186             
10187             this.toolTip = new Roo.bootstrap.Tooltip({
10188                 cls : 'roo-form-error-popover',
10189                 alignment : {
10190                     'left' : ['r-l', [-2,0], 'right'],
10191                     'right' : ['l-r', [2,0], 'left'],
10192                     'bottom' : ['tl-bl', [0,2], 'top'],
10193                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10194                 }
10195             });
10196             
10197             this.toolTip.render(Roo.get(document.body));
10198
10199             this.toolTip.el.enableDisplayMode("block");
10200             
10201             Roo.get(document.body).on('click', function(){
10202                 this.unmask();
10203             }, this);
10204             
10205             Roo.get(document.body).on('touchstart', function(){
10206                 this.unmask();
10207             }, this);
10208             
10209             this.isApplied = true
10210         },
10211         
10212         mask : function(form, target)
10213         {
10214             this.form = form;
10215             
10216             this.target = target;
10217             
10218             if(!this.form.errorMask || !target.el){
10219                 return;
10220             }
10221             
10222             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10223             
10224             Roo.log(scrollable);
10225             
10226             var ot = this.target.el.calcOffsetsTo(scrollable);
10227             
10228             var scrollTo = ot[1] - this.form.maskOffset;
10229             
10230             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10231             
10232             scrollable.scrollTo('top', scrollTo);
10233             
10234             var box = this.target.el.getBox();
10235             Roo.log(box);
10236             var zIndex = Roo.bootstrap.Modal.zIndex++;
10237
10238             
10239             this.maskEl.top.setStyle('position', 'absolute');
10240             this.maskEl.top.setStyle('z-index', zIndex);
10241             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10242             this.maskEl.top.setLeft(0);
10243             this.maskEl.top.setTop(0);
10244             this.maskEl.top.show();
10245             
10246             this.maskEl.left.setStyle('position', 'absolute');
10247             this.maskEl.left.setStyle('z-index', zIndex);
10248             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10249             this.maskEl.left.setLeft(0);
10250             this.maskEl.left.setTop(box.y - this.padding);
10251             this.maskEl.left.show();
10252
10253             this.maskEl.bottom.setStyle('position', 'absolute');
10254             this.maskEl.bottom.setStyle('z-index', zIndex);
10255             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10256             this.maskEl.bottom.setLeft(0);
10257             this.maskEl.bottom.setTop(box.bottom + this.padding);
10258             this.maskEl.bottom.show();
10259
10260             this.maskEl.right.setStyle('position', 'absolute');
10261             this.maskEl.right.setStyle('z-index', zIndex);
10262             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10263             this.maskEl.right.setLeft(box.right + this.padding);
10264             this.maskEl.right.setTop(box.y - this.padding);
10265             this.maskEl.right.show();
10266
10267             this.toolTip.bindEl = this.target.el;
10268
10269             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10270
10271             var tip = this.target.blankText;
10272
10273             if(this.target.getValue() !== '' ) {
10274                 
10275                 if (this.target.invalidText.length) {
10276                     tip = this.target.invalidText;
10277                 } else if (this.target.regexText.length){
10278                     tip = this.target.regexText;
10279                 }
10280             }
10281
10282             this.toolTip.show(tip);
10283
10284             this.intervalID = window.setInterval(function() {
10285                 Roo.bootstrap.Form.popover.unmask();
10286             }, 10000);
10287
10288             window.onwheel = function(){ return false;};
10289             
10290             (function(){ this.isMasked = true; }).defer(500, this);
10291             
10292         },
10293         
10294         unmask : function()
10295         {
10296             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10297                 return;
10298             }
10299             
10300             this.maskEl.top.setStyle('position', 'absolute');
10301             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10302             this.maskEl.top.hide();
10303
10304             this.maskEl.left.setStyle('position', 'absolute');
10305             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10306             this.maskEl.left.hide();
10307
10308             this.maskEl.bottom.setStyle('position', 'absolute');
10309             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10310             this.maskEl.bottom.hide();
10311
10312             this.maskEl.right.setStyle('position', 'absolute');
10313             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10314             this.maskEl.right.hide();
10315             
10316             this.toolTip.hide();
10317             
10318             this.toolTip.el.hide();
10319             
10320             window.onwheel = function(){ return true;};
10321             
10322             if(this.intervalID){
10323                 window.clearInterval(this.intervalID);
10324                 this.intervalID = false;
10325             }
10326             
10327             this.isMasked = false;
10328             
10329         }
10330         
10331     }
10332     
10333 });
10334
10335 /*
10336  * Based on:
10337  * Ext JS Library 1.1.1
10338  * Copyright(c) 2006-2007, Ext JS, LLC.
10339  *
10340  * Originally Released Under LGPL - original licence link has changed is not relivant.
10341  *
10342  * Fork - LGPL
10343  * <script type="text/javascript">
10344  */
10345 /**
10346  * @class Roo.form.VTypes
10347  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10348  * @singleton
10349  */
10350 Roo.form.VTypes = function(){
10351     // closure these in so they are only created once.
10352     var alpha = /^[a-zA-Z_]+$/;
10353     var alphanum = /^[a-zA-Z0-9_]+$/;
10354     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10355     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10356
10357     // All these messages and functions are configurable
10358     return {
10359         /**
10360          * The function used to validate email addresses
10361          * @param {String} value The email address
10362          */
10363         'email' : function(v){
10364             return email.test(v);
10365         },
10366         /**
10367          * The error text to display when the email validation function returns false
10368          * @type String
10369          */
10370         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10371         /**
10372          * The keystroke filter mask to be applied on email input
10373          * @type RegExp
10374          */
10375         'emailMask' : /[a-z0-9_\.\-@]/i,
10376
10377         /**
10378          * The function used to validate URLs
10379          * @param {String} value The URL
10380          */
10381         'url' : function(v){
10382             return url.test(v);
10383         },
10384         /**
10385          * The error text to display when the url validation function returns false
10386          * @type String
10387          */
10388         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10389         
10390         /**
10391          * The function used to validate alpha values
10392          * @param {String} value The value
10393          */
10394         'alpha' : function(v){
10395             return alpha.test(v);
10396         },
10397         /**
10398          * The error text to display when the alpha validation function returns false
10399          * @type String
10400          */
10401         'alphaText' : 'This field should only contain letters and _',
10402         /**
10403          * The keystroke filter mask to be applied on alpha input
10404          * @type RegExp
10405          */
10406         'alphaMask' : /[a-z_]/i,
10407
10408         /**
10409          * The function used to validate alphanumeric values
10410          * @param {String} value The value
10411          */
10412         'alphanum' : function(v){
10413             return alphanum.test(v);
10414         },
10415         /**
10416          * The error text to display when the alphanumeric validation function returns false
10417          * @type String
10418          */
10419         'alphanumText' : 'This field should only contain letters, numbers and _',
10420         /**
10421          * The keystroke filter mask to be applied on alphanumeric input
10422          * @type RegExp
10423          */
10424         'alphanumMask' : /[a-z0-9_]/i
10425     };
10426 }();/*
10427  * - LGPL
10428  *
10429  * Input
10430  * 
10431  */
10432
10433 /**
10434  * @class Roo.bootstrap.Input
10435  * @extends Roo.bootstrap.Component
10436  * Bootstrap Input class
10437  * @cfg {Boolean} disabled is it disabled
10438  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10439  * @cfg {String} name name of the input
10440  * @cfg {string} fieldLabel - the label associated
10441  * @cfg {string} placeholder - placeholder to put in text.
10442  * @cfg {string}  before - input group add on before
10443  * @cfg {string} after - input group add on after
10444  * @cfg {string} size - (lg|sm) or leave empty..
10445  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10446  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10447  * @cfg {Number} md colspan out of 12 for computer-sized screens
10448  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10449  * @cfg {string} value default value of the input
10450  * @cfg {Number} labelWidth set the width of label 
10451  * @cfg {Number} labellg set the width of label (1-12)
10452  * @cfg {Number} labelmd set the width of label (1-12)
10453  * @cfg {Number} labelsm set the width of label (1-12)
10454  * @cfg {Number} labelxs set the width of label (1-12)
10455  * @cfg {String} labelAlign (top|left)
10456  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10457  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10458  * @cfg {String} indicatorpos (left|right) default left
10459  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10460  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10461  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10462
10463  * @cfg {String} align (left|center|right) Default left
10464  * @cfg {Boolean} forceFeedback (true|false) Default false
10465  * 
10466  * @constructor
10467  * Create a new Input
10468  * @param {Object} config The config object
10469  */
10470
10471 Roo.bootstrap.Input = function(config){
10472     
10473     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10474     
10475     this.addEvents({
10476         /**
10477          * @event focus
10478          * Fires when this field receives input focus.
10479          * @param {Roo.form.Field} this
10480          */
10481         focus : true,
10482         /**
10483          * @event blur
10484          * Fires when this field loses input focus.
10485          * @param {Roo.form.Field} this
10486          */
10487         blur : true,
10488         /**
10489          * @event specialkey
10490          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10491          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10492          * @param {Roo.form.Field} this
10493          * @param {Roo.EventObject} e The event object
10494          */
10495         specialkey : true,
10496         /**
10497          * @event change
10498          * Fires just before the field blurs if the field value has changed.
10499          * @param {Roo.form.Field} this
10500          * @param {Mixed} newValue The new value
10501          * @param {Mixed} oldValue The original value
10502          */
10503         change : true,
10504         /**
10505          * @event invalid
10506          * Fires after the field has been marked as invalid.
10507          * @param {Roo.form.Field} this
10508          * @param {String} msg The validation message
10509          */
10510         invalid : true,
10511         /**
10512          * @event valid
10513          * Fires after the field has been validated with no errors.
10514          * @param {Roo.form.Field} this
10515          */
10516         valid : true,
10517          /**
10518          * @event keyup
10519          * Fires after the key up
10520          * @param {Roo.form.Field} this
10521          * @param {Roo.EventObject}  e The event Object
10522          */
10523         keyup : true,
10524         /**
10525          * @event paste
10526          * Fires after the user pastes into input
10527          * @param {Roo.form.Field} this
10528          * @param {Roo.EventObject}  e The event Object
10529          */
10530         paste : true
10531     });
10532 };
10533
10534 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10535      /**
10536      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10537       automatic validation (defaults to "keyup").
10538      */
10539     validationEvent : "keyup",
10540      /**
10541      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10542      */
10543     validateOnBlur : true,
10544     /**
10545      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10546      */
10547     validationDelay : 250,
10548      /**
10549      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10550      */
10551     focusClass : "x-form-focus",  // not needed???
10552     
10553        
10554     /**
10555      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10556      */
10557     invalidClass : "has-warning",
10558     
10559     /**
10560      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10561      */
10562     validClass : "has-success",
10563     
10564     /**
10565      * @cfg {Boolean} hasFeedback (true|false) default true
10566      */
10567     hasFeedback : true,
10568     
10569     /**
10570      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10571      */
10572     invalidFeedbackClass : "glyphicon-warning-sign",
10573     
10574     /**
10575      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10576      */
10577     validFeedbackClass : "glyphicon-ok",
10578     
10579     /**
10580      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10581      */
10582     selectOnFocus : false,
10583     
10584      /**
10585      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10586      */
10587     maskRe : null,
10588        /**
10589      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10590      */
10591     vtype : null,
10592     
10593       /**
10594      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10595      */
10596     disableKeyFilter : false,
10597     
10598        /**
10599      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10600      */
10601     disabled : false,
10602      /**
10603      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10604      */
10605     allowBlank : true,
10606     /**
10607      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10608      */
10609     blankText : "Please complete this mandatory field",
10610     
10611      /**
10612      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10613      */
10614     minLength : 0,
10615     /**
10616      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10617      */
10618     maxLength : Number.MAX_VALUE,
10619     /**
10620      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10621      */
10622     minLengthText : "The minimum length for this field is {0}",
10623     /**
10624      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10625      */
10626     maxLengthText : "The maximum length for this field is {0}",
10627   
10628     
10629     /**
10630      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10631      * If available, this function will be called only after the basic validators all return true, and will be passed the
10632      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10633      */
10634     validator : null,
10635     /**
10636      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10637      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10638      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10639      */
10640     regex : null,
10641     /**
10642      * @cfg {String} regexText -- Depricated - use Invalid Text
10643      */
10644     regexText : "",
10645     
10646     /**
10647      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10648      */
10649     invalidText : "",
10650     
10651     
10652     
10653     autocomplete: false,
10654     
10655     
10656     fieldLabel : '',
10657     inputType : 'text',
10658     
10659     name : false,
10660     placeholder: false,
10661     before : false,
10662     after : false,
10663     size : false,
10664     hasFocus : false,
10665     preventMark: false,
10666     isFormField : true,
10667     value : '',
10668     labelWidth : 2,
10669     labelAlign : false,
10670     readOnly : false,
10671     align : false,
10672     formatedValue : false,
10673     forceFeedback : false,
10674     
10675     indicatorpos : 'left',
10676     
10677     labellg : 0,
10678     labelmd : 0,
10679     labelsm : 0,
10680     labelxs : 0,
10681     
10682     capture : '',
10683     accept : '',
10684     
10685     parentLabelAlign : function()
10686     {
10687         var parent = this;
10688         while (parent.parent()) {
10689             parent = parent.parent();
10690             if (typeof(parent.labelAlign) !='undefined') {
10691                 return parent.labelAlign;
10692             }
10693         }
10694         return 'left';
10695         
10696     },
10697     
10698     getAutoCreate : function()
10699     {
10700         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10701         
10702         var id = Roo.id();
10703         
10704         var cfg = {};
10705         
10706         if(this.inputType != 'hidden'){
10707             cfg.cls = 'form-group' //input-group
10708         }
10709         
10710         var input =  {
10711             tag: 'input',
10712             id : id,
10713             type : this.inputType,
10714             value : this.value,
10715             cls : 'form-control',
10716             placeholder : this.placeholder || '',
10717             autocomplete : this.autocomplete || 'new-password'
10718         };
10719         if (this.inputType == 'file') {
10720             input.style = 'overflow:hidden'; // why not in CSS?
10721         }
10722         
10723         if(this.capture.length){
10724             input.capture = this.capture;
10725         }
10726         
10727         if(this.accept.length){
10728             input.accept = this.accept + "/*";
10729         }
10730         
10731         if(this.align){
10732             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10733         }
10734         
10735         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10736             input.maxLength = this.maxLength;
10737         }
10738         
10739         if (this.disabled) {
10740             input.disabled=true;
10741         }
10742         
10743         if (this.readOnly) {
10744             input.readonly=true;
10745         }
10746         
10747         if (this.name) {
10748             input.name = this.name;
10749         }
10750         
10751         if (this.size) {
10752             input.cls += ' input-' + this.size;
10753         }
10754         
10755         var settings=this;
10756         ['xs','sm','md','lg'].map(function(size){
10757             if (settings[size]) {
10758                 cfg.cls += ' col-' + size + '-' + settings[size];
10759             }
10760         });
10761         
10762         var inputblock = input;
10763         
10764         var feedback = {
10765             tag: 'span',
10766             cls: 'glyphicon form-control-feedback'
10767         };
10768             
10769         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10770             
10771             inputblock = {
10772                 cls : 'has-feedback',
10773                 cn :  [
10774                     input,
10775                     feedback
10776                 ] 
10777             };  
10778         }
10779         
10780         if (this.before || this.after) {
10781             
10782             inputblock = {
10783                 cls : 'input-group',
10784                 cn :  [] 
10785             };
10786             
10787             if (this.before && typeof(this.before) == 'string') {
10788                 
10789                 inputblock.cn.push({
10790                     tag :'span',
10791                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10792                     html : this.before
10793                 });
10794             }
10795             if (this.before && typeof(this.before) == 'object') {
10796                 this.before = Roo.factory(this.before);
10797                 
10798                 inputblock.cn.push({
10799                     tag :'span',
10800                     cls : 'roo-input-before input-group-prepend   input-group-' +
10801                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10802                 });
10803             }
10804             
10805             inputblock.cn.push(input);
10806             
10807             if (this.after && typeof(this.after) == 'string') {
10808                 inputblock.cn.push({
10809                     tag :'span',
10810                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10811                     html : this.after
10812                 });
10813             }
10814             if (this.after && typeof(this.after) == 'object') {
10815                 this.after = Roo.factory(this.after);
10816                 
10817                 inputblock.cn.push({
10818                     tag :'span',
10819                     cls : 'roo-input-after input-group-append  input-group-' +
10820                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10821                 });
10822             }
10823             
10824             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10825                 inputblock.cls += ' has-feedback';
10826                 inputblock.cn.push(feedback);
10827             }
10828         };
10829         var indicator = {
10830             tag : 'i',
10831             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10832             tooltip : 'This field is required'
10833         };
10834         if (this.allowBlank ) {
10835             indicator.style = this.allowBlank ? ' display:none' : '';
10836         }
10837         if (align ==='left' && this.fieldLabel.length) {
10838             
10839             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10840             
10841             cfg.cn = [
10842                 indicator,
10843                 {
10844                     tag: 'label',
10845                     'for' :  id,
10846                     cls : 'control-label col-form-label',
10847                     html : this.fieldLabel
10848
10849                 },
10850                 {
10851                     cls : "", 
10852                     cn: [
10853                         inputblock
10854                     ]
10855                 }
10856             ];
10857             
10858             var labelCfg = cfg.cn[1];
10859             var contentCfg = cfg.cn[2];
10860             
10861             if(this.indicatorpos == 'right'){
10862                 cfg.cn = [
10863                     {
10864                         tag: 'label',
10865                         'for' :  id,
10866                         cls : 'control-label col-form-label',
10867                         cn : [
10868                             {
10869                                 tag : 'span',
10870                                 html : this.fieldLabel
10871                             },
10872                             indicator
10873                         ]
10874                     },
10875                     {
10876                         cls : "",
10877                         cn: [
10878                             inputblock
10879                         ]
10880                     }
10881
10882                 ];
10883                 
10884                 labelCfg = cfg.cn[0];
10885                 contentCfg = cfg.cn[1];
10886             
10887             }
10888             
10889             if(this.labelWidth > 12){
10890                 labelCfg.style = "width: " + this.labelWidth + 'px';
10891             }
10892             
10893             if(this.labelWidth < 13 && this.labelmd == 0){
10894                 this.labelmd = this.labelWidth;
10895             }
10896             
10897             if(this.labellg > 0){
10898                 labelCfg.cls += ' col-lg-' + this.labellg;
10899                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10900             }
10901             
10902             if(this.labelmd > 0){
10903                 labelCfg.cls += ' col-md-' + this.labelmd;
10904                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10905             }
10906             
10907             if(this.labelsm > 0){
10908                 labelCfg.cls += ' col-sm-' + this.labelsm;
10909                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10910             }
10911             
10912             if(this.labelxs > 0){
10913                 labelCfg.cls += ' col-xs-' + this.labelxs;
10914                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10915             }
10916             
10917             
10918         } else if ( this.fieldLabel.length) {
10919                 
10920             
10921             
10922             cfg.cn = [
10923                 {
10924                     tag : 'i',
10925                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10926                     tooltip : 'This field is required',
10927                     style : this.allowBlank ? ' display:none' : '' 
10928                 },
10929                 {
10930                     tag: 'label',
10931                    //cls : 'input-group-addon',
10932                     html : this.fieldLabel
10933
10934                 },
10935
10936                inputblock
10937
10938            ];
10939            
10940            if(this.indicatorpos == 'right'){
10941        
10942                 cfg.cn = [
10943                     {
10944                         tag: 'label',
10945                        //cls : 'input-group-addon',
10946                         html : this.fieldLabel
10947
10948                     },
10949                     {
10950                         tag : 'i',
10951                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10952                         tooltip : 'This field is required',
10953                         style : this.allowBlank ? ' display:none' : '' 
10954                     },
10955
10956                    inputblock
10957
10958                ];
10959
10960             }
10961
10962         } else {
10963             
10964             cfg.cn = [
10965
10966                     inputblock
10967
10968             ];
10969                 
10970                 
10971         };
10972         
10973         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10974            cfg.cls += ' navbar-form';
10975         }
10976         
10977         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10978             // on BS4 we do this only if not form 
10979             cfg.cls += ' navbar-form';
10980             cfg.tag = 'li';
10981         }
10982         
10983         return cfg;
10984         
10985     },
10986     /**
10987      * return the real input element.
10988      */
10989     inputEl: function ()
10990     {
10991         return this.el.select('input.form-control',true).first();
10992     },
10993     
10994     tooltipEl : function()
10995     {
10996         return this.inputEl();
10997     },
10998     
10999     indicatorEl : function()
11000     {
11001         if (Roo.bootstrap.version == 4) {
11002             return false; // not enabled in v4 yet.
11003         }
11004         
11005         var indicator = this.el.select('i.roo-required-indicator',true).first();
11006         
11007         if(!indicator){
11008             return false;
11009         }
11010         
11011         return indicator;
11012         
11013     },
11014     
11015     setDisabled : function(v)
11016     {
11017         var i  = this.inputEl().dom;
11018         if (!v) {
11019             i.removeAttribute('disabled');
11020             return;
11021             
11022         }
11023         i.setAttribute('disabled','true');
11024     },
11025     initEvents : function()
11026     {
11027           
11028         this.inputEl().on("keydown" , this.fireKey,  this);
11029         this.inputEl().on("focus", this.onFocus,  this);
11030         this.inputEl().on("blur", this.onBlur,  this);
11031         
11032         this.inputEl().relayEvent('keyup', this);
11033         this.inputEl().relayEvent('paste', this);
11034         
11035         this.indicator = this.indicatorEl();
11036         
11037         if(this.indicator){
11038             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11039         }
11040  
11041         // reference to original value for reset
11042         this.originalValue = this.getValue();
11043         //Roo.form.TextField.superclass.initEvents.call(this);
11044         if(this.validationEvent == 'keyup'){
11045             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11046             this.inputEl().on('keyup', this.filterValidation, this);
11047         }
11048         else if(this.validationEvent !== false){
11049             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11050         }
11051         
11052         if(this.selectOnFocus){
11053             this.on("focus", this.preFocus, this);
11054             
11055         }
11056         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11057             this.inputEl().on("keypress", this.filterKeys, this);
11058         } else {
11059             this.inputEl().relayEvent('keypress', this);
11060         }
11061        /* if(this.grow){
11062             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11063             this.el.on("click", this.autoSize,  this);
11064         }
11065         */
11066         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11067             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11068         }
11069         
11070         if (typeof(this.before) == 'object') {
11071             this.before.render(this.el.select('.roo-input-before',true).first());
11072         }
11073         if (typeof(this.after) == 'object') {
11074             this.after.render(this.el.select('.roo-input-after',true).first());
11075         }
11076         
11077         this.inputEl().on('change', this.onChange, this);
11078         
11079     },
11080     filterValidation : function(e){
11081         if(!e.isNavKeyPress()){
11082             this.validationTask.delay(this.validationDelay);
11083         }
11084     },
11085      /**
11086      * Validates the field value
11087      * @return {Boolean} True if the value is valid, else false
11088      */
11089     validate : function(){
11090         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11091         if(this.disabled || this.validateValue(this.getRawValue())){
11092             this.markValid();
11093             return true;
11094         }
11095         
11096         this.markInvalid();
11097         return false;
11098     },
11099     
11100     
11101     /**
11102      * Validates a value according to the field's validation rules and marks the field as invalid
11103      * if the validation fails
11104      * @param {Mixed} value The value to validate
11105      * @return {Boolean} True if the value is valid, else false
11106      */
11107     validateValue : function(value)
11108     {
11109         if(this.getVisibilityEl().hasClass('hidden')){
11110             return true;
11111         }
11112         
11113         if(value.length < 1)  { // if it's blank
11114             if(this.allowBlank){
11115                 return true;
11116             }
11117             return false;
11118         }
11119         
11120         if(value.length < this.minLength){
11121             return false;
11122         }
11123         if(value.length > this.maxLength){
11124             return false;
11125         }
11126         if(this.vtype){
11127             var vt = Roo.form.VTypes;
11128             if(!vt[this.vtype](value, this)){
11129                 return false;
11130             }
11131         }
11132         if(typeof this.validator == "function"){
11133             var msg = this.validator(value);
11134             if(msg !== true){
11135                 return false;
11136             }
11137             if (typeof(msg) == 'string') {
11138                 this.invalidText = msg;
11139             }
11140         }
11141         
11142         if(this.regex && !this.regex.test(value)){
11143             return false;
11144         }
11145         
11146         return true;
11147     },
11148     
11149      // private
11150     fireKey : function(e){
11151         //Roo.log('field ' + e.getKey());
11152         if(e.isNavKeyPress()){
11153             this.fireEvent("specialkey", this, e);
11154         }
11155     },
11156     focus : function (selectText){
11157         if(this.rendered){
11158             this.inputEl().focus();
11159             if(selectText === true){
11160                 this.inputEl().dom.select();
11161             }
11162         }
11163         return this;
11164     } ,
11165     
11166     onFocus : function(){
11167         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11168            // this.el.addClass(this.focusClass);
11169         }
11170         if(!this.hasFocus){
11171             this.hasFocus = true;
11172             this.startValue = this.getValue();
11173             this.fireEvent("focus", this);
11174         }
11175     },
11176     
11177     beforeBlur : Roo.emptyFn,
11178
11179     
11180     // private
11181     onBlur : function(){
11182         this.beforeBlur();
11183         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11184             //this.el.removeClass(this.focusClass);
11185         }
11186         this.hasFocus = false;
11187         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11188             this.validate();
11189         }
11190         var v = this.getValue();
11191         if(String(v) !== String(this.startValue)){
11192             this.fireEvent('change', this, v, this.startValue);
11193         }
11194         this.fireEvent("blur", this);
11195     },
11196     
11197     onChange : function(e)
11198     {
11199         var v = this.getValue();
11200         if(String(v) !== String(this.startValue)){
11201             this.fireEvent('change', this, v, this.startValue);
11202         }
11203         
11204     },
11205     
11206     /**
11207      * Resets the current field value to the originally loaded value and clears any validation messages
11208      */
11209     reset : function(){
11210         this.setValue(this.originalValue);
11211         this.validate();
11212     },
11213      /**
11214      * Returns the name of the field
11215      * @return {Mixed} name The name field
11216      */
11217     getName: function(){
11218         return this.name;
11219     },
11220      /**
11221      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11222      * @return {Mixed} value The field value
11223      */
11224     getValue : function(){
11225         
11226         var v = this.inputEl().getValue();
11227         
11228         return v;
11229     },
11230     /**
11231      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11232      * @return {Mixed} value The field value
11233      */
11234     getRawValue : function(){
11235         var v = this.inputEl().getValue();
11236         
11237         return v;
11238     },
11239     
11240     /**
11241      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11242      * @param {Mixed} value The value to set
11243      */
11244     setRawValue : function(v){
11245         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11246     },
11247     
11248     selectText : function(start, end){
11249         var v = this.getRawValue();
11250         if(v.length > 0){
11251             start = start === undefined ? 0 : start;
11252             end = end === undefined ? v.length : end;
11253             var d = this.inputEl().dom;
11254             if(d.setSelectionRange){
11255                 d.setSelectionRange(start, end);
11256             }else if(d.createTextRange){
11257                 var range = d.createTextRange();
11258                 range.moveStart("character", start);
11259                 range.moveEnd("character", v.length-end);
11260                 range.select();
11261             }
11262         }
11263     },
11264     
11265     /**
11266      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11267      * @param {Mixed} value The value to set
11268      */
11269     setValue : function(v){
11270         this.value = v;
11271         if(this.rendered){
11272             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11273             this.validate();
11274         }
11275     },
11276     
11277     /*
11278     processValue : function(value){
11279         if(this.stripCharsRe){
11280             var newValue = value.replace(this.stripCharsRe, '');
11281             if(newValue !== value){
11282                 this.setRawValue(newValue);
11283                 return newValue;
11284             }
11285         }
11286         return value;
11287     },
11288   */
11289     preFocus : function(){
11290         
11291         if(this.selectOnFocus){
11292             this.inputEl().dom.select();
11293         }
11294     },
11295     filterKeys : function(e){
11296         var k = e.getKey();
11297         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11298             return;
11299         }
11300         var c = e.getCharCode(), cc = String.fromCharCode(c);
11301         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11302             return;
11303         }
11304         if(!this.maskRe.test(cc)){
11305             e.stopEvent();
11306         }
11307     },
11308      /**
11309      * Clear any invalid styles/messages for this field
11310      */
11311     clearInvalid : function(){
11312         
11313         if(!this.el || this.preventMark){ // not rendered
11314             return;
11315         }
11316         
11317         
11318         this.el.removeClass([this.invalidClass, 'is-invalid']);
11319         
11320         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11321             
11322             var feedback = this.el.select('.form-control-feedback', true).first();
11323             
11324             if(feedback){
11325                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11326             }
11327             
11328         }
11329         
11330         if(this.indicator){
11331             this.indicator.removeClass('visible');
11332             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11333         }
11334         
11335         this.fireEvent('valid', this);
11336     },
11337     
11338      /**
11339      * Mark this field as valid
11340      */
11341     markValid : function()
11342     {
11343         if(!this.el  || this.preventMark){ // not rendered...
11344             return;
11345         }
11346         
11347         this.el.removeClass([this.invalidClass, this.validClass]);
11348         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11349
11350         var feedback = this.el.select('.form-control-feedback', true).first();
11351             
11352         if(feedback){
11353             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11354         }
11355         
11356         if(this.indicator){
11357             this.indicator.removeClass('visible');
11358             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11359         }
11360         
11361         if(this.disabled){
11362             return;
11363         }
11364         
11365            
11366         if(this.allowBlank && !this.getRawValue().length){
11367             return;
11368         }
11369         if (Roo.bootstrap.version == 3) {
11370             this.el.addClass(this.validClass);
11371         } else {
11372             this.inputEl().addClass('is-valid');
11373         }
11374
11375         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11376             
11377             var feedback = this.el.select('.form-control-feedback', true).first();
11378             
11379             if(feedback){
11380                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11381                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11382             }
11383             
11384         }
11385         
11386         this.fireEvent('valid', this);
11387     },
11388     
11389      /**
11390      * Mark this field as invalid
11391      * @param {String} msg The validation message
11392      */
11393     markInvalid : function(msg)
11394     {
11395         if(!this.el  || this.preventMark){ // not rendered
11396             return;
11397         }
11398         
11399         this.el.removeClass([this.invalidClass, this.validClass]);
11400         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11401         
11402         var feedback = this.el.select('.form-control-feedback', true).first();
11403             
11404         if(feedback){
11405             this.el.select('.form-control-feedback', true).first().removeClass(
11406                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11407         }
11408
11409         if(this.disabled){
11410             return;
11411         }
11412         
11413         if(this.allowBlank && !this.getRawValue().length){
11414             return;
11415         }
11416         
11417         if(this.indicator){
11418             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11419             this.indicator.addClass('visible');
11420         }
11421         if (Roo.bootstrap.version == 3) {
11422             this.el.addClass(this.invalidClass);
11423         } else {
11424             this.inputEl().addClass('is-invalid');
11425         }
11426         
11427         
11428         
11429         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11430             
11431             var feedback = this.el.select('.form-control-feedback', true).first();
11432             
11433             if(feedback){
11434                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11435                 
11436                 if(this.getValue().length || this.forceFeedback){
11437                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11438                 }
11439                 
11440             }
11441             
11442         }
11443         
11444         this.fireEvent('invalid', this, msg);
11445     },
11446     // private
11447     SafariOnKeyDown : function(event)
11448     {
11449         // this is a workaround for a password hang bug on chrome/ webkit.
11450         if (this.inputEl().dom.type != 'password') {
11451             return;
11452         }
11453         
11454         var isSelectAll = false;
11455         
11456         if(this.inputEl().dom.selectionEnd > 0){
11457             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11458         }
11459         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11460             event.preventDefault();
11461             this.setValue('');
11462             return;
11463         }
11464         
11465         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11466             
11467             event.preventDefault();
11468             // this is very hacky as keydown always get's upper case.
11469             //
11470             var cc = String.fromCharCode(event.getCharCode());
11471             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11472             
11473         }
11474     },
11475     adjustWidth : function(tag, w){
11476         tag = tag.toLowerCase();
11477         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11478             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11479                 if(tag == 'input'){
11480                     return w + 2;
11481                 }
11482                 if(tag == 'textarea'){
11483                     return w-2;
11484                 }
11485             }else if(Roo.isOpera){
11486                 if(tag == 'input'){
11487                     return w + 2;
11488                 }
11489                 if(tag == 'textarea'){
11490                     return w-2;
11491                 }
11492             }
11493         }
11494         return w;
11495     },
11496     
11497     setFieldLabel : function(v)
11498     {
11499         if(!this.rendered){
11500             return;
11501         }
11502         
11503         if(this.indicatorEl()){
11504             var ar = this.el.select('label > span',true);
11505             
11506             if (ar.elements.length) {
11507                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11508                 this.fieldLabel = v;
11509                 return;
11510             }
11511             
11512             var br = this.el.select('label',true);
11513             
11514             if(br.elements.length) {
11515                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11516                 this.fieldLabel = v;
11517                 return;
11518             }
11519             
11520             Roo.log('Cannot Found any of label > span || label in input');
11521             return;
11522         }
11523         
11524         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11525         this.fieldLabel = v;
11526         
11527         
11528     }
11529 });
11530
11531  
11532 /*
11533  * - LGPL
11534  *
11535  * Input
11536  * 
11537  */
11538
11539 /**
11540  * @class Roo.bootstrap.TextArea
11541  * @extends Roo.bootstrap.Input
11542  * Bootstrap TextArea class
11543  * @cfg {Number} cols Specifies the visible width of a text area
11544  * @cfg {Number} rows Specifies the visible number of lines in a text area
11545  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11546  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11547  * @cfg {string} html text
11548  * 
11549  * @constructor
11550  * Create a new TextArea
11551  * @param {Object} config The config object
11552  */
11553
11554 Roo.bootstrap.TextArea = function(config){
11555     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11556    
11557 };
11558
11559 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11560      
11561     cols : false,
11562     rows : 5,
11563     readOnly : false,
11564     warp : 'soft',
11565     resize : false,
11566     value: false,
11567     html: false,
11568     
11569     getAutoCreate : function(){
11570         
11571         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11572         
11573         var id = Roo.id();
11574         
11575         var cfg = {};
11576         
11577         if(this.inputType != 'hidden'){
11578             cfg.cls = 'form-group' //input-group
11579         }
11580         
11581         var input =  {
11582             tag: 'textarea',
11583             id : id,
11584             warp : this.warp,
11585             rows : this.rows,
11586             value : this.value || '',
11587             html: this.html || '',
11588             cls : 'form-control',
11589             placeholder : this.placeholder || '' 
11590             
11591         };
11592         
11593         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11594             input.maxLength = this.maxLength;
11595         }
11596         
11597         if(this.resize){
11598             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11599         }
11600         
11601         if(this.cols){
11602             input.cols = this.cols;
11603         }
11604         
11605         if (this.readOnly) {
11606             input.readonly = true;
11607         }
11608         
11609         if (this.name) {
11610             input.name = this.name;
11611         }
11612         
11613         if (this.size) {
11614             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11615         }
11616         
11617         var settings=this;
11618         ['xs','sm','md','lg'].map(function(size){
11619             if (settings[size]) {
11620                 cfg.cls += ' col-' + size + '-' + settings[size];
11621             }
11622         });
11623         
11624         var inputblock = input;
11625         
11626         if(this.hasFeedback && !this.allowBlank){
11627             
11628             var feedback = {
11629                 tag: 'span',
11630                 cls: 'glyphicon form-control-feedback'
11631             };
11632
11633             inputblock = {
11634                 cls : 'has-feedback',
11635                 cn :  [
11636                     input,
11637                     feedback
11638                 ] 
11639             };  
11640         }
11641         
11642         
11643         if (this.before || this.after) {
11644             
11645             inputblock = {
11646                 cls : 'input-group',
11647                 cn :  [] 
11648             };
11649             if (this.before) {
11650                 inputblock.cn.push({
11651                     tag :'span',
11652                     cls : 'input-group-addon',
11653                     html : this.before
11654                 });
11655             }
11656             
11657             inputblock.cn.push(input);
11658             
11659             if(this.hasFeedback && !this.allowBlank){
11660                 inputblock.cls += ' has-feedback';
11661                 inputblock.cn.push(feedback);
11662             }
11663             
11664             if (this.after) {
11665                 inputblock.cn.push({
11666                     tag :'span',
11667                     cls : 'input-group-addon',
11668                     html : this.after
11669                 });
11670             }
11671             
11672         }
11673         
11674         if (align ==='left' && this.fieldLabel.length) {
11675             cfg.cn = [
11676                 {
11677                     tag: 'label',
11678                     'for' :  id,
11679                     cls : 'control-label',
11680                     html : this.fieldLabel
11681                 },
11682                 {
11683                     cls : "",
11684                     cn: [
11685                         inputblock
11686                     ]
11687                 }
11688
11689             ];
11690             
11691             if(this.labelWidth > 12){
11692                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11693             }
11694
11695             if(this.labelWidth < 13 && this.labelmd == 0){
11696                 this.labelmd = this.labelWidth;
11697             }
11698
11699             if(this.labellg > 0){
11700                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11701                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11702             }
11703
11704             if(this.labelmd > 0){
11705                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11706                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11707             }
11708
11709             if(this.labelsm > 0){
11710                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11711                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11712             }
11713
11714             if(this.labelxs > 0){
11715                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11716                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11717             }
11718             
11719         } else if ( this.fieldLabel.length) {
11720             cfg.cn = [
11721
11722                {
11723                    tag: 'label',
11724                    //cls : 'input-group-addon',
11725                    html : this.fieldLabel
11726
11727                },
11728
11729                inputblock
11730
11731            ];
11732
11733         } else {
11734
11735             cfg.cn = [
11736
11737                 inputblock
11738
11739             ];
11740                 
11741         }
11742         
11743         if (this.disabled) {
11744             input.disabled=true;
11745         }
11746         
11747         return cfg;
11748         
11749     },
11750     /**
11751      * return the real textarea element.
11752      */
11753     inputEl: function ()
11754     {
11755         return this.el.select('textarea.form-control',true).first();
11756     },
11757     
11758     /**
11759      * Clear any invalid styles/messages for this field
11760      */
11761     clearInvalid : function()
11762     {
11763         
11764         if(!this.el || this.preventMark){ // not rendered
11765             return;
11766         }
11767         
11768         var label = this.el.select('label', true).first();
11769         var icon = this.el.select('i.fa-star', true).first();
11770         
11771         if(label && icon){
11772             icon.remove();
11773         }
11774         this.el.removeClass( this.validClass);
11775         this.inputEl().removeClass('is-invalid');
11776          
11777         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11778             
11779             var feedback = this.el.select('.form-control-feedback', true).first();
11780             
11781             if(feedback){
11782                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11783             }
11784             
11785         }
11786         
11787         this.fireEvent('valid', this);
11788     },
11789     
11790      /**
11791      * Mark this field as valid
11792      */
11793     markValid : function()
11794     {
11795         if(!this.el  || this.preventMark){ // not rendered
11796             return;
11797         }
11798         
11799         this.el.removeClass([this.invalidClass, this.validClass]);
11800         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11801         
11802         var feedback = this.el.select('.form-control-feedback', true).first();
11803             
11804         if(feedback){
11805             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11806         }
11807
11808         if(this.disabled || this.allowBlank){
11809             return;
11810         }
11811         
11812         var label = this.el.select('label', true).first();
11813         var icon = this.el.select('i.fa-star', true).first();
11814         
11815         if(label && icon){
11816             icon.remove();
11817         }
11818         if (Roo.bootstrap.version == 3) {
11819             this.el.addClass(this.validClass);
11820         } else {
11821             this.inputEl().addClass('is-valid');
11822         }
11823         
11824         
11825         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11826             
11827             var feedback = this.el.select('.form-control-feedback', true).first();
11828             
11829             if(feedback){
11830                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11831                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11832             }
11833             
11834         }
11835         
11836         this.fireEvent('valid', this);
11837     },
11838     
11839      /**
11840      * Mark this field as invalid
11841      * @param {String} msg The validation message
11842      */
11843     markInvalid : function(msg)
11844     {
11845         if(!this.el  || this.preventMark){ // not rendered
11846             return;
11847         }
11848         
11849         this.el.removeClass([this.invalidClass, this.validClass]);
11850         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11851         
11852         var feedback = this.el.select('.form-control-feedback', true).first();
11853             
11854         if(feedback){
11855             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11856         }
11857
11858         if(this.disabled || this.allowBlank){
11859             return;
11860         }
11861         
11862         var label = this.el.select('label', true).first();
11863         var icon = this.el.select('i.fa-star', true).first();
11864         
11865         if(!this.getValue().length && label && !icon){
11866             this.el.createChild({
11867                 tag : 'i',
11868                 cls : 'text-danger fa fa-lg fa-star',
11869                 tooltip : 'This field is required',
11870                 style : 'margin-right:5px;'
11871             }, label, true);
11872         }
11873         
11874         if (Roo.bootstrap.version == 3) {
11875             this.el.addClass(this.invalidClass);
11876         } else {
11877             this.inputEl().addClass('is-invalid');
11878         }
11879         
11880         // fixme ... this may be depricated need to test..
11881         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11882             
11883             var feedback = this.el.select('.form-control-feedback', true).first();
11884             
11885             if(feedback){
11886                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11887                 
11888                 if(this.getValue().length || this.forceFeedback){
11889                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11890                 }
11891                 
11892             }
11893             
11894         }
11895         
11896         this.fireEvent('invalid', this, msg);
11897     }
11898 });
11899
11900  
11901 /*
11902  * - LGPL
11903  *
11904  * trigger field - base class for combo..
11905  * 
11906  */
11907  
11908 /**
11909  * @class Roo.bootstrap.TriggerField
11910  * @extends Roo.bootstrap.Input
11911  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11912  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11913  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11914  * for which you can provide a custom implementation.  For example:
11915  * <pre><code>
11916 var trigger = new Roo.bootstrap.TriggerField();
11917 trigger.onTriggerClick = myTriggerFn;
11918 trigger.applyTo('my-field');
11919 </code></pre>
11920  *
11921  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11922  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11923  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11924  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11925  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11926
11927  * @constructor
11928  * Create a new TriggerField.
11929  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11930  * to the base TextField)
11931  */
11932 Roo.bootstrap.TriggerField = function(config){
11933     this.mimicing = false;
11934     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11935 };
11936
11937 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11938     /**
11939      * @cfg {String} triggerClass A CSS class to apply to the trigger
11940      */
11941      /**
11942      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11943      */
11944     hideTrigger:false,
11945
11946     /**
11947      * @cfg {Boolean} removable (true|false) special filter default false
11948      */
11949     removable : false,
11950     
11951     /** @cfg {Boolean} grow @hide */
11952     /** @cfg {Number} growMin @hide */
11953     /** @cfg {Number} growMax @hide */
11954
11955     /**
11956      * @hide 
11957      * @method
11958      */
11959     autoSize: Roo.emptyFn,
11960     // private
11961     monitorTab : true,
11962     // private
11963     deferHeight : true,
11964
11965     
11966     actionMode : 'wrap',
11967     
11968     caret : false,
11969     
11970     
11971     getAutoCreate : function(){
11972        
11973         var align = this.labelAlign || this.parentLabelAlign();
11974         
11975         var id = Roo.id();
11976         
11977         var cfg = {
11978             cls: 'form-group' //input-group
11979         };
11980         
11981         
11982         var input =  {
11983             tag: 'input',
11984             id : id,
11985             type : this.inputType,
11986             cls : 'form-control',
11987             autocomplete: 'new-password',
11988             placeholder : this.placeholder || '' 
11989             
11990         };
11991         if (this.name) {
11992             input.name = this.name;
11993         }
11994         if (this.size) {
11995             input.cls += ' input-' + this.size;
11996         }
11997         
11998         if (this.disabled) {
11999             input.disabled=true;
12000         }
12001         
12002         var inputblock = input;
12003         
12004         if(this.hasFeedback && !this.allowBlank){
12005             
12006             var feedback = {
12007                 tag: 'span',
12008                 cls: 'glyphicon form-control-feedback'
12009             };
12010             
12011             if(this.removable && !this.editable  ){
12012                 inputblock = {
12013                     cls : 'has-feedback',
12014                     cn :  [
12015                         inputblock,
12016                         {
12017                             tag: 'button',
12018                             html : 'x',
12019                             cls : 'roo-combo-removable-btn close'
12020                         },
12021                         feedback
12022                     ] 
12023                 };
12024             } else {
12025                 inputblock = {
12026                     cls : 'has-feedback',
12027                     cn :  [
12028                         inputblock,
12029                         feedback
12030                     ] 
12031                 };
12032             }
12033
12034         } else {
12035             if(this.removable && !this.editable ){
12036                 inputblock = {
12037                     cls : 'roo-removable',
12038                     cn :  [
12039                         inputblock,
12040                         {
12041                             tag: 'button',
12042                             html : 'x',
12043                             cls : 'roo-combo-removable-btn close'
12044                         }
12045                     ] 
12046                 };
12047             }
12048         }
12049         
12050         if (this.before || this.after) {
12051             
12052             inputblock = {
12053                 cls : 'input-group',
12054                 cn :  [] 
12055             };
12056             if (this.before) {
12057                 inputblock.cn.push({
12058                     tag :'span',
12059                     cls : 'input-group-addon input-group-prepend input-group-text',
12060                     html : this.before
12061                 });
12062             }
12063             
12064             inputblock.cn.push(input);
12065             
12066             if(this.hasFeedback && !this.allowBlank){
12067                 inputblock.cls += ' has-feedback';
12068                 inputblock.cn.push(feedback);
12069             }
12070             
12071             if (this.after) {
12072                 inputblock.cn.push({
12073                     tag :'span',
12074                     cls : 'input-group-addon input-group-append input-group-text',
12075                     html : this.after
12076                 });
12077             }
12078             
12079         };
12080         
12081       
12082         
12083         var ibwrap = inputblock;
12084         
12085         if(this.multiple){
12086             ibwrap = {
12087                 tag: 'ul',
12088                 cls: 'roo-select2-choices',
12089                 cn:[
12090                     {
12091                         tag: 'li',
12092                         cls: 'roo-select2-search-field',
12093                         cn: [
12094
12095                             inputblock
12096                         ]
12097                     }
12098                 ]
12099             };
12100                 
12101         }
12102         
12103         var combobox = {
12104             cls: 'roo-select2-container input-group',
12105             cn: [
12106                  {
12107                     tag: 'input',
12108                     type : 'hidden',
12109                     cls: 'form-hidden-field'
12110                 },
12111                 ibwrap
12112             ]
12113         };
12114         
12115         if(!this.multiple && this.showToggleBtn){
12116             
12117             var caret = {
12118                         tag: 'span',
12119                         cls: 'caret'
12120              };
12121             if (this.caret != false) {
12122                 caret = {
12123                      tag: 'i',
12124                      cls: 'fa fa-' + this.caret
12125                 };
12126                 
12127             }
12128             
12129             combobox.cn.push({
12130                 tag :'span',
12131                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12132                 cn : [
12133                     Roo.bootstrap.version == 3 ? caret : '',
12134                     {
12135                         tag: 'span',
12136                         cls: 'combobox-clear',
12137                         cn  : [
12138                             {
12139                                 tag : 'i',
12140                                 cls: 'icon-remove'
12141                             }
12142                         ]
12143                     }
12144                 ]
12145
12146             })
12147         }
12148         
12149         if(this.multiple){
12150             combobox.cls += ' roo-select2-container-multi';
12151         }
12152          var indicator = {
12153             tag : 'i',
12154             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12155             tooltip : 'This field is required'
12156         };
12157         if (Roo.bootstrap.version == 4) {
12158             indicator = {
12159                 tag : 'i',
12160                 style : 'display:none'
12161             };
12162         }
12163         
12164         
12165         if (align ==='left' && this.fieldLabel.length) {
12166             
12167             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12168
12169             cfg.cn = [
12170                 indicator,
12171                 {
12172                     tag: 'label',
12173                     'for' :  id,
12174                     cls : 'control-label',
12175                     html : this.fieldLabel
12176
12177                 },
12178                 {
12179                     cls : "", 
12180                     cn: [
12181                         combobox
12182                     ]
12183                 }
12184
12185             ];
12186             
12187             var labelCfg = cfg.cn[1];
12188             var contentCfg = cfg.cn[2];
12189             
12190             if(this.indicatorpos == 'right'){
12191                 cfg.cn = [
12192                     {
12193                         tag: 'label',
12194                         'for' :  id,
12195                         cls : 'control-label',
12196                         cn : [
12197                             {
12198                                 tag : 'span',
12199                                 html : this.fieldLabel
12200                             },
12201                             indicator
12202                         ]
12203                     },
12204                     {
12205                         cls : "", 
12206                         cn: [
12207                             combobox
12208                         ]
12209                     }
12210
12211                 ];
12212                 
12213                 labelCfg = cfg.cn[0];
12214                 contentCfg = cfg.cn[1];
12215             }
12216             
12217             if(this.labelWidth > 12){
12218                 labelCfg.style = "width: " + this.labelWidth + 'px';
12219             }
12220             
12221             if(this.labelWidth < 13 && this.labelmd == 0){
12222                 this.labelmd = this.labelWidth;
12223             }
12224             
12225             if(this.labellg > 0){
12226                 labelCfg.cls += ' col-lg-' + this.labellg;
12227                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12228             }
12229             
12230             if(this.labelmd > 0){
12231                 labelCfg.cls += ' col-md-' + this.labelmd;
12232                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12233             }
12234             
12235             if(this.labelsm > 0){
12236                 labelCfg.cls += ' col-sm-' + this.labelsm;
12237                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12238             }
12239             
12240             if(this.labelxs > 0){
12241                 labelCfg.cls += ' col-xs-' + this.labelxs;
12242                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12243             }
12244             
12245         } else if ( this.fieldLabel.length) {
12246 //                Roo.log(" label");
12247             cfg.cn = [
12248                 indicator,
12249                {
12250                    tag: 'label',
12251                    //cls : 'input-group-addon',
12252                    html : this.fieldLabel
12253
12254                },
12255
12256                combobox
12257
12258             ];
12259             
12260             if(this.indicatorpos == 'right'){
12261                 
12262                 cfg.cn = [
12263                     {
12264                        tag: 'label',
12265                        cn : [
12266                            {
12267                                tag : 'span',
12268                                html : this.fieldLabel
12269                            },
12270                            indicator
12271                        ]
12272
12273                     },
12274                     combobox
12275
12276                 ];
12277
12278             }
12279
12280         } else {
12281             
12282 //                Roo.log(" no label && no align");
12283                 cfg = combobox
12284                      
12285                 
12286         }
12287         
12288         var settings=this;
12289         ['xs','sm','md','lg'].map(function(size){
12290             if (settings[size]) {
12291                 cfg.cls += ' col-' + size + '-' + settings[size];
12292             }
12293         });
12294         
12295         return cfg;
12296         
12297     },
12298     
12299     
12300     
12301     // private
12302     onResize : function(w, h){
12303 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12304 //        if(typeof w == 'number'){
12305 //            var x = w - this.trigger.getWidth();
12306 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12307 //            this.trigger.setStyle('left', x+'px');
12308 //        }
12309     },
12310
12311     // private
12312     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12313
12314     // private
12315     getResizeEl : function(){
12316         return this.inputEl();
12317     },
12318
12319     // private
12320     getPositionEl : function(){
12321         return this.inputEl();
12322     },
12323
12324     // private
12325     alignErrorIcon : function(){
12326         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12327     },
12328
12329     // private
12330     initEvents : function(){
12331         
12332         this.createList();
12333         
12334         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12335         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12336         if(!this.multiple && this.showToggleBtn){
12337             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12338             if(this.hideTrigger){
12339                 this.trigger.setDisplayed(false);
12340             }
12341             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12342         }
12343         
12344         if(this.multiple){
12345             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12346         }
12347         
12348         if(this.removable && !this.editable && !this.tickable){
12349             var close = this.closeTriggerEl();
12350             
12351             if(close){
12352                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12353                 close.on('click', this.removeBtnClick, this, close);
12354             }
12355         }
12356         
12357         //this.trigger.addClassOnOver('x-form-trigger-over');
12358         //this.trigger.addClassOnClick('x-form-trigger-click');
12359         
12360         //if(!this.width){
12361         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12362         //}
12363     },
12364     
12365     closeTriggerEl : function()
12366     {
12367         var close = this.el.select('.roo-combo-removable-btn', true).first();
12368         return close ? close : false;
12369     },
12370     
12371     removeBtnClick : function(e, h, el)
12372     {
12373         e.preventDefault();
12374         
12375         if(this.fireEvent("remove", this) !== false){
12376             this.reset();
12377             this.fireEvent("afterremove", this)
12378         }
12379     },
12380     
12381     createList : function()
12382     {
12383         this.list = Roo.get(document.body).createChild({
12384             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12385             cls: 'typeahead typeahead-long dropdown-menu shadow',
12386             style: 'display:none'
12387         });
12388         
12389         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12390         
12391     },
12392
12393     // private
12394     initTrigger : function(){
12395        
12396     },
12397
12398     // private
12399     onDestroy : function(){
12400         if(this.trigger){
12401             this.trigger.removeAllListeners();
12402           //  this.trigger.remove();
12403         }
12404         //if(this.wrap){
12405         //    this.wrap.remove();
12406         //}
12407         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12408     },
12409
12410     // private
12411     onFocus : function(){
12412         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12413         /*
12414         if(!this.mimicing){
12415             this.wrap.addClass('x-trigger-wrap-focus');
12416             this.mimicing = true;
12417             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12418             if(this.monitorTab){
12419                 this.el.on("keydown", this.checkTab, this);
12420             }
12421         }
12422         */
12423     },
12424
12425     // private
12426     checkTab : function(e){
12427         if(e.getKey() == e.TAB){
12428             this.triggerBlur();
12429         }
12430     },
12431
12432     // private
12433     onBlur : function(){
12434         // do nothing
12435     },
12436
12437     // private
12438     mimicBlur : function(e, t){
12439         /*
12440         if(!this.wrap.contains(t) && this.validateBlur()){
12441             this.triggerBlur();
12442         }
12443         */
12444     },
12445
12446     // private
12447     triggerBlur : function(){
12448         this.mimicing = false;
12449         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12450         if(this.monitorTab){
12451             this.el.un("keydown", this.checkTab, this);
12452         }
12453         //this.wrap.removeClass('x-trigger-wrap-focus');
12454         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12455     },
12456
12457     // private
12458     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12459     validateBlur : function(e, t){
12460         return true;
12461     },
12462
12463     // private
12464     onDisable : function(){
12465         this.inputEl().dom.disabled = true;
12466         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12467         //if(this.wrap){
12468         //    this.wrap.addClass('x-item-disabled');
12469         //}
12470     },
12471
12472     // private
12473     onEnable : function(){
12474         this.inputEl().dom.disabled = false;
12475         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12476         //if(this.wrap){
12477         //    this.el.removeClass('x-item-disabled');
12478         //}
12479     },
12480
12481     // private
12482     onShow : function(){
12483         var ae = this.getActionEl();
12484         
12485         if(ae){
12486             ae.dom.style.display = '';
12487             ae.dom.style.visibility = 'visible';
12488         }
12489     },
12490
12491     // private
12492     
12493     onHide : function(){
12494         var ae = this.getActionEl();
12495         ae.dom.style.display = 'none';
12496     },
12497
12498     /**
12499      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12500      * by an implementing function.
12501      * @method
12502      * @param {EventObject} e
12503      */
12504     onTriggerClick : Roo.emptyFn
12505 });
12506  
12507 /*
12508 * Licence: LGPL
12509 */
12510
12511 /**
12512  * @class Roo.bootstrap.CardUploader
12513  * @extends Roo.bootstrap.Button
12514  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12515  * @cfg {Number} errorTimeout default 3000
12516  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12517  * @cfg {Array}  html The button text.
12518
12519  *
12520  * @constructor
12521  * Create a new CardUploader
12522  * @param {Object} config The config object
12523  */
12524
12525 Roo.bootstrap.CardUploader = function(config){
12526     
12527  
12528     
12529     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12530     
12531     
12532     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12533         return r.data.id
12534      });
12535     
12536      this.addEvents({
12537          // raw events
12538         /**
12539          * @event preview
12540          * When a image is clicked on - and needs to display a slideshow or similar..
12541          * @param {Roo.bootstrap.Card} this
12542          * @param {Object} The image information data 
12543          *
12544          */
12545         'preview' : true,
12546          /**
12547          * @event download
12548          * When a the download link is clicked
12549          * @param {Roo.bootstrap.Card} this
12550          * @param {Object} The image information data  contains 
12551          */
12552         'download' : true
12553         
12554     });
12555 };
12556  
12557 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12558     
12559      
12560     errorTimeout : 3000,
12561      
12562     images : false,
12563    
12564     fileCollection : false,
12565     allowBlank : true,
12566     
12567     getAutoCreate : function()
12568     {
12569         
12570         var cfg =  {
12571             cls :'form-group' ,
12572             cn : [
12573                
12574                 {
12575                     tag: 'label',
12576                    //cls : 'input-group-addon',
12577                     html : this.fieldLabel
12578
12579                 },
12580
12581                 {
12582                     tag: 'input',
12583                     type : 'hidden',
12584                     name : this.name,
12585                     value : this.value,
12586                     cls : 'd-none  form-control'
12587                 },
12588                 
12589                 {
12590                     tag: 'input',
12591                     multiple : 'multiple',
12592                     type : 'file',
12593                     cls : 'd-none  roo-card-upload-selector'
12594                 },
12595                 
12596                 {
12597                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12598                 },
12599                 {
12600                     cls : 'card-columns roo-card-uploader-container'
12601                 }
12602
12603             ]
12604         };
12605            
12606          
12607         return cfg;
12608     },
12609     
12610     getChildContainer : function() /// what children are added to.
12611     {
12612         return this.containerEl;
12613     },
12614    
12615     getButtonContainer : function() /// what children are added to.
12616     {
12617         return this.el.select(".roo-card-uploader-button-container").first();
12618     },
12619    
12620     initEvents : function()
12621     {
12622         
12623         Roo.bootstrap.Input.prototype.initEvents.call(this);
12624         
12625         var t = this;
12626         this.addxtype({
12627             xns: Roo.bootstrap,
12628
12629             xtype : 'Button',
12630             container_method : 'getButtonContainer' ,            
12631             html :  this.html, // fix changable?
12632             cls : 'w-100 ',
12633             listeners : {
12634                 'click' : function(btn, e) {
12635                     t.onClick(e);
12636                 }
12637             }
12638         });
12639         
12640         
12641         
12642         
12643         this.urlAPI = (window.createObjectURL && window) || 
12644                                 (window.URL && URL.revokeObjectURL && URL) || 
12645                                 (window.webkitURL && webkitURL);
12646                         
12647          
12648          
12649          
12650         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12651         
12652         this.selectorEl.on('change', this.onFileSelected, this);
12653         if (this.images) {
12654             var t = this;
12655             this.images.forEach(function(img) {
12656                 t.addCard(img)
12657             });
12658             this.images = false;
12659         }
12660         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12661          
12662        
12663     },
12664     
12665    
12666     onClick : function(e)
12667     {
12668         e.preventDefault();
12669          
12670         this.selectorEl.dom.click();
12671          
12672     },
12673     
12674     onFileSelected : function(e)
12675     {
12676         e.preventDefault();
12677         
12678         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12679             return;
12680         }
12681         
12682         Roo.each(this.selectorEl.dom.files, function(file){    
12683             this.addFile(file);
12684         }, this);
12685          
12686     },
12687     
12688       
12689     
12690       
12691     
12692     addFile : function(file)
12693     {
12694            
12695         if(typeof(file) === 'string'){
12696             throw "Add file by name?"; // should not happen
12697             return;
12698         }
12699         
12700         if(!file || !this.urlAPI){
12701             return;
12702         }
12703         
12704         // file;
12705         // file.type;
12706         
12707         var _this = this;
12708         
12709         
12710         var url = _this.urlAPI.createObjectURL( file);
12711            
12712         this.addCard({
12713             id : Roo.bootstrap.CardUploader.ID--,
12714             is_uploaded : false,
12715             src : url,
12716             srcfile : file,
12717             title : file.name,
12718             mimetype : file.type,
12719             preview : false,
12720             is_deleted : 0
12721         });
12722         
12723     },
12724     
12725     /**
12726      * addCard - add an Attachment to the uploader
12727      * @param data - the data about the image to upload
12728      *
12729      * {
12730           id : 123
12731           title : "Title of file",
12732           is_uploaded : false,
12733           src : "http://.....",
12734           srcfile : { the File upload object },
12735           mimetype : file.type,
12736           preview : false,
12737           is_deleted : 0
12738           .. any other data...
12739         }
12740      *
12741      * 
12742     */
12743     
12744     addCard : function (data)
12745     {
12746         // hidden input element?
12747         // if the file is not an image...
12748         //then we need to use something other that and header_image
12749         var t = this;
12750         //   remove.....
12751         var footer = [
12752             {
12753                 xns : Roo.bootstrap,
12754                 xtype : 'CardFooter',
12755                  items: [
12756                     {
12757                         xns : Roo.bootstrap,
12758                         xtype : 'Element',
12759                         cls : 'd-flex',
12760                         items : [
12761                             
12762                             {
12763                                 xns : Roo.bootstrap,
12764                                 xtype : 'Button',
12765                                 html : String.format("<small>{0}</small>", data.title),
12766                                 cls : 'col-10 text-left',
12767                                 size: 'sm',
12768                                 weight: 'link',
12769                                 fa : 'download',
12770                                 listeners : {
12771                                     click : function() {
12772                                      
12773                                         t.fireEvent( "download", t, data );
12774                                     }
12775                                 }
12776                             },
12777                           
12778                             {
12779                                 xns : Roo.bootstrap,
12780                                 xtype : 'Button',
12781                                 style: 'max-height: 28px; ',
12782                                 size : 'sm',
12783                                 weight: 'danger',
12784                                 cls : 'col-2',
12785                                 fa : 'times',
12786                                 listeners : {
12787                                     click : function() {
12788                                         t.removeCard(data.id)
12789                                     }
12790                                 }
12791                             }
12792                         ]
12793                     }
12794                     
12795                 ] 
12796             }
12797             
12798         ];
12799         
12800         var cn = this.addxtype(
12801             {
12802                  
12803                 xns : Roo.bootstrap,
12804                 xtype : 'Card',
12805                 closeable : true,
12806                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12807                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12808                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12809                 data : data,
12810                 html : false,
12811                  
12812                 items : footer,
12813                 initEvents : function() {
12814                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12815                     var card = this;
12816                     this.imgEl = this.el.select('.card-img-top').first();
12817                     if (this.imgEl) {
12818                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12819                         this.imgEl.set({ 'pointer' : 'cursor' });
12820                                   
12821                     }
12822                     this.getCardFooter().addClass('p-1');
12823                     
12824                   
12825                 }
12826                 
12827             }
12828         );
12829         // dont' really need ot update items.
12830         // this.items.push(cn);
12831         this.fileCollection.add(cn);
12832         
12833         if (!data.srcfile) {
12834             this.updateInput();
12835             return;
12836         }
12837             
12838         var _t = this;
12839         var reader = new FileReader();
12840         reader.addEventListener("load", function() {  
12841             data.srcdata =  reader.result;
12842             _t.updateInput();
12843         });
12844         reader.readAsDataURL(data.srcfile);
12845         
12846         
12847         
12848     },
12849     removeCard : function(id)
12850     {
12851         
12852         var card  = this.fileCollection.get(id);
12853         card.data.is_deleted = 1;
12854         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12855         //this.fileCollection.remove(card);
12856         //this.items = this.items.filter(function(e) { return e != card });
12857         // dont' really need ot update items.
12858         card.el.dom.parentNode.removeChild(card.el.dom);
12859         this.updateInput();
12860
12861         
12862     },
12863     reset: function()
12864     {
12865         this.fileCollection.each(function(card) {
12866             if (card.el.dom && card.el.dom.parentNode) {
12867                 card.el.dom.parentNode.removeChild(card.el.dom);
12868             }
12869         });
12870         this.fileCollection.clear();
12871         this.updateInput();
12872     },
12873     
12874     updateInput : function()
12875     {
12876          var data = [];
12877         this.fileCollection.each(function(e) {
12878             data.push(e.data);
12879             
12880         });
12881         this.inputEl().dom.value = JSON.stringify(data);
12882         
12883         
12884         
12885     }
12886     
12887     
12888 });
12889
12890
12891 Roo.bootstrap.CardUploader.ID = -1;/*
12892  * Based on:
12893  * Ext JS Library 1.1.1
12894  * Copyright(c) 2006-2007, Ext JS, LLC.
12895  *
12896  * Originally Released Under LGPL - original licence link has changed is not relivant.
12897  *
12898  * Fork - LGPL
12899  * <script type="text/javascript">
12900  */
12901
12902
12903 /**
12904  * @class Roo.data.SortTypes
12905  * @singleton
12906  * Defines the default sorting (casting?) comparison functions used when sorting data.
12907  */
12908 Roo.data.SortTypes = {
12909     /**
12910      * Default sort that does nothing
12911      * @param {Mixed} s The value being converted
12912      * @return {Mixed} The comparison value
12913      */
12914     none : function(s){
12915         return s;
12916     },
12917     
12918     /**
12919      * The regular expression used to strip tags
12920      * @type {RegExp}
12921      * @property
12922      */
12923     stripTagsRE : /<\/?[^>]+>/gi,
12924     
12925     /**
12926      * Strips all HTML tags to sort on text only
12927      * @param {Mixed} s The value being converted
12928      * @return {String} The comparison value
12929      */
12930     asText : function(s){
12931         return String(s).replace(this.stripTagsRE, "");
12932     },
12933     
12934     /**
12935      * Strips all HTML tags to sort on text only - Case insensitive
12936      * @param {Mixed} s The value being converted
12937      * @return {String} The comparison value
12938      */
12939     asUCText : function(s){
12940         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12941     },
12942     
12943     /**
12944      * Case insensitive string
12945      * @param {Mixed} s The value being converted
12946      * @return {String} The comparison value
12947      */
12948     asUCString : function(s) {
12949         return String(s).toUpperCase();
12950     },
12951     
12952     /**
12953      * Date sorting
12954      * @param {Mixed} s The value being converted
12955      * @return {Number} The comparison value
12956      */
12957     asDate : function(s) {
12958         if(!s){
12959             return 0;
12960         }
12961         if(s instanceof Date){
12962             return s.getTime();
12963         }
12964         return Date.parse(String(s));
12965     },
12966     
12967     /**
12968      * Float sorting
12969      * @param {Mixed} s The value being converted
12970      * @return {Float} The comparison value
12971      */
12972     asFloat : function(s) {
12973         var val = parseFloat(String(s).replace(/,/g, ""));
12974         if(isNaN(val)) {
12975             val = 0;
12976         }
12977         return val;
12978     },
12979     
12980     /**
12981      * Integer sorting
12982      * @param {Mixed} s The value being converted
12983      * @return {Number} The comparison value
12984      */
12985     asInt : function(s) {
12986         var val = parseInt(String(s).replace(/,/g, ""));
12987         if(isNaN(val)) {
12988             val = 0;
12989         }
12990         return val;
12991     }
12992 };/*
12993  * Based on:
12994  * Ext JS Library 1.1.1
12995  * Copyright(c) 2006-2007, Ext JS, LLC.
12996  *
12997  * Originally Released Under LGPL - original licence link has changed is not relivant.
12998  *
12999  * Fork - LGPL
13000  * <script type="text/javascript">
13001  */
13002
13003 /**
13004 * @class Roo.data.Record
13005  * Instances of this class encapsulate both record <em>definition</em> information, and record
13006  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
13007  * to access Records cached in an {@link Roo.data.Store} object.<br>
13008  * <p>
13009  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
13010  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
13011  * objects.<br>
13012  * <p>
13013  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
13014  * @constructor
13015  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
13016  * {@link #create}. The parameters are the same.
13017  * @param {Array} data An associative Array of data values keyed by the field name.
13018  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
13019  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
13020  * not specified an integer id is generated.
13021  */
13022 Roo.data.Record = function(data, id){
13023     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
13024     this.data = data;
13025 };
13026
13027 /**
13028  * Generate a constructor for a specific record layout.
13029  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
13030  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
13031  * Each field definition object may contain the following properties: <ul>
13032  * <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,
13033  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
13034  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
13035  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
13036  * is being used, then this is a string containing the javascript expression to reference the data relative to 
13037  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
13038  * to the data item relative to the record element. If the mapping expression is the same as the field name,
13039  * this may be omitted.</p></li>
13040  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
13041  * <ul><li>auto (Default, implies no conversion)</li>
13042  * <li>string</li>
13043  * <li>int</li>
13044  * <li>float</li>
13045  * <li>boolean</li>
13046  * <li>date</li></ul></p></li>
13047  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
13048  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
13049  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13050  * by the Reader into an object that will be stored in the Record. It is passed the
13051  * following parameters:<ul>
13052  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13053  * </ul></p></li>
13054  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13055  * </ul>
13056  * <br>usage:<br><pre><code>
13057 var TopicRecord = Roo.data.Record.create(
13058     {name: 'title', mapping: 'topic_title'},
13059     {name: 'author', mapping: 'username'},
13060     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13061     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13062     {name: 'lastPoster', mapping: 'user2'},
13063     {name: 'excerpt', mapping: 'post_text'}
13064 );
13065
13066 var myNewRecord = new TopicRecord({
13067     title: 'Do my job please',
13068     author: 'noobie',
13069     totalPosts: 1,
13070     lastPost: new Date(),
13071     lastPoster: 'Animal',
13072     excerpt: 'No way dude!'
13073 });
13074 myStore.add(myNewRecord);
13075 </code></pre>
13076  * @method create
13077  * @static
13078  */
13079 Roo.data.Record.create = function(o){
13080     var f = function(){
13081         f.superclass.constructor.apply(this, arguments);
13082     };
13083     Roo.extend(f, Roo.data.Record);
13084     var p = f.prototype;
13085     p.fields = new Roo.util.MixedCollection(false, function(field){
13086         return field.name;
13087     });
13088     for(var i = 0, len = o.length; i < len; i++){
13089         p.fields.add(new Roo.data.Field(o[i]));
13090     }
13091     f.getField = function(name){
13092         return p.fields.get(name);  
13093     };
13094     return f;
13095 };
13096
13097 Roo.data.Record.AUTO_ID = 1000;
13098 Roo.data.Record.EDIT = 'edit';
13099 Roo.data.Record.REJECT = 'reject';
13100 Roo.data.Record.COMMIT = 'commit';
13101
13102 Roo.data.Record.prototype = {
13103     /**
13104      * Readonly flag - true if this record has been modified.
13105      * @type Boolean
13106      */
13107     dirty : false,
13108     editing : false,
13109     error: null,
13110     modified: null,
13111
13112     // private
13113     join : function(store){
13114         this.store = store;
13115     },
13116
13117     /**
13118      * Set the named field to the specified value.
13119      * @param {String} name The name of the field to set.
13120      * @param {Object} value The value to set the field to.
13121      */
13122     set : function(name, value){
13123         if(this.data[name] == value){
13124             return;
13125         }
13126         this.dirty = true;
13127         if(!this.modified){
13128             this.modified = {};
13129         }
13130         if(typeof this.modified[name] == 'undefined'){
13131             this.modified[name] = this.data[name];
13132         }
13133         this.data[name] = value;
13134         if(!this.editing && this.store){
13135             this.store.afterEdit(this);
13136         }       
13137     },
13138
13139     /**
13140      * Get the value of the named field.
13141      * @param {String} name The name of the field to get the value of.
13142      * @return {Object} The value of the field.
13143      */
13144     get : function(name){
13145         return this.data[name]; 
13146     },
13147
13148     // private
13149     beginEdit : function(){
13150         this.editing = true;
13151         this.modified = {}; 
13152     },
13153
13154     // private
13155     cancelEdit : function(){
13156         this.editing = false;
13157         delete this.modified;
13158     },
13159
13160     // private
13161     endEdit : function(){
13162         this.editing = false;
13163         if(this.dirty && this.store){
13164             this.store.afterEdit(this);
13165         }
13166     },
13167
13168     /**
13169      * Usually called by the {@link Roo.data.Store} which owns the Record.
13170      * Rejects all changes made to the Record since either creation, or the last commit operation.
13171      * Modified fields are reverted to their original values.
13172      * <p>
13173      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13174      * of reject operations.
13175      */
13176     reject : function(){
13177         var m = this.modified;
13178         for(var n in m){
13179             if(typeof m[n] != "function"){
13180                 this.data[n] = m[n];
13181             }
13182         }
13183         this.dirty = false;
13184         delete this.modified;
13185         this.editing = false;
13186         if(this.store){
13187             this.store.afterReject(this);
13188         }
13189     },
13190
13191     /**
13192      * Usually called by the {@link Roo.data.Store} which owns the Record.
13193      * Commits all changes made to the Record since either creation, or the last commit operation.
13194      * <p>
13195      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13196      * of commit operations.
13197      */
13198     commit : function(){
13199         this.dirty = false;
13200         delete this.modified;
13201         this.editing = false;
13202         if(this.store){
13203             this.store.afterCommit(this);
13204         }
13205     },
13206
13207     // private
13208     hasError : function(){
13209         return this.error != null;
13210     },
13211
13212     // private
13213     clearError : function(){
13214         this.error = null;
13215     },
13216
13217     /**
13218      * Creates a copy of this record.
13219      * @param {String} id (optional) A new record id if you don't want to use this record's id
13220      * @return {Record}
13221      */
13222     copy : function(newId) {
13223         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13224     }
13225 };/*
13226  * Based on:
13227  * Ext JS Library 1.1.1
13228  * Copyright(c) 2006-2007, Ext JS, LLC.
13229  *
13230  * Originally Released Under LGPL - original licence link has changed is not relivant.
13231  *
13232  * Fork - LGPL
13233  * <script type="text/javascript">
13234  */
13235
13236
13237
13238 /**
13239  * @class Roo.data.Store
13240  * @extends Roo.util.Observable
13241  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13242  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13243  * <p>
13244  * 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
13245  * has no knowledge of the format of the data returned by the Proxy.<br>
13246  * <p>
13247  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13248  * instances from the data object. These records are cached and made available through accessor functions.
13249  * @constructor
13250  * Creates a new Store.
13251  * @param {Object} config A config object containing the objects needed for the Store to access data,
13252  * and read the data into Records.
13253  */
13254 Roo.data.Store = function(config){
13255     this.data = new Roo.util.MixedCollection(false);
13256     this.data.getKey = function(o){
13257         return o.id;
13258     };
13259     this.baseParams = {};
13260     // private
13261     this.paramNames = {
13262         "start" : "start",
13263         "limit" : "limit",
13264         "sort" : "sort",
13265         "dir" : "dir",
13266         "multisort" : "_multisort"
13267     };
13268
13269     if(config && config.data){
13270         this.inlineData = config.data;
13271         delete config.data;
13272     }
13273
13274     Roo.apply(this, config);
13275     
13276     if(this.reader){ // reader passed
13277         this.reader = Roo.factory(this.reader, Roo.data);
13278         this.reader.xmodule = this.xmodule || false;
13279         if(!this.recordType){
13280             this.recordType = this.reader.recordType;
13281         }
13282         if(this.reader.onMetaChange){
13283             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13284         }
13285     }
13286
13287     if(this.recordType){
13288         this.fields = this.recordType.prototype.fields;
13289     }
13290     this.modified = [];
13291
13292     this.addEvents({
13293         /**
13294          * @event datachanged
13295          * Fires when the data cache has changed, and a widget which is using this Store
13296          * as a Record cache should refresh its view.
13297          * @param {Store} this
13298          */
13299         datachanged : true,
13300         /**
13301          * @event metachange
13302          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13303          * @param {Store} this
13304          * @param {Object} meta The JSON metadata
13305          */
13306         metachange : true,
13307         /**
13308          * @event add
13309          * Fires when Records have been added to the Store
13310          * @param {Store} this
13311          * @param {Roo.data.Record[]} records The array of Records added
13312          * @param {Number} index The index at which the record(s) were added
13313          */
13314         add : true,
13315         /**
13316          * @event remove
13317          * Fires when a Record has been removed from the Store
13318          * @param {Store} this
13319          * @param {Roo.data.Record} record The Record that was removed
13320          * @param {Number} index The index at which the record was removed
13321          */
13322         remove : true,
13323         /**
13324          * @event update
13325          * Fires when a Record has been updated
13326          * @param {Store} this
13327          * @param {Roo.data.Record} record The Record that was updated
13328          * @param {String} operation The update operation being performed.  Value may be one of:
13329          * <pre><code>
13330  Roo.data.Record.EDIT
13331  Roo.data.Record.REJECT
13332  Roo.data.Record.COMMIT
13333          * </code></pre>
13334          */
13335         update : true,
13336         /**
13337          * @event clear
13338          * Fires when the data cache has been cleared.
13339          * @param {Store} this
13340          */
13341         clear : true,
13342         /**
13343          * @event beforeload
13344          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13345          * the load action will be canceled.
13346          * @param {Store} this
13347          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13348          */
13349         beforeload : true,
13350         /**
13351          * @event beforeloadadd
13352          * Fires after a new set of Records has been loaded.
13353          * @param {Store} this
13354          * @param {Roo.data.Record[]} records The Records that were loaded
13355          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13356          */
13357         beforeloadadd : true,
13358         /**
13359          * @event load
13360          * Fires after a new set of Records has been loaded, before they are added to the store.
13361          * @param {Store} this
13362          * @param {Roo.data.Record[]} records The Records that were loaded
13363          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13364          * @params {Object} return from reader
13365          */
13366         load : true,
13367         /**
13368          * @event loadexception
13369          * Fires if an exception occurs in the Proxy during loading.
13370          * Called with the signature of the Proxy's "loadexception" event.
13371          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13372          * 
13373          * @param {Proxy} 
13374          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13375          * @param {Object} load options 
13376          * @param {Object} jsonData from your request (normally this contains the Exception)
13377          */
13378         loadexception : true
13379     });
13380     
13381     if(this.proxy){
13382         this.proxy = Roo.factory(this.proxy, Roo.data);
13383         this.proxy.xmodule = this.xmodule || false;
13384         this.relayEvents(this.proxy,  ["loadexception"]);
13385     }
13386     this.sortToggle = {};
13387     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13388
13389     Roo.data.Store.superclass.constructor.call(this);
13390
13391     if(this.inlineData){
13392         this.loadData(this.inlineData);
13393         delete this.inlineData;
13394     }
13395 };
13396
13397 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13398      /**
13399     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13400     * without a remote query - used by combo/forms at present.
13401     */
13402     
13403     /**
13404     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13405     */
13406     /**
13407     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13408     */
13409     /**
13410     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13411     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13412     */
13413     /**
13414     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13415     * on any HTTP request
13416     */
13417     /**
13418     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13419     */
13420     /**
13421     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13422     */
13423     multiSort: false,
13424     /**
13425     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13426     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13427     */
13428     remoteSort : false,
13429
13430     /**
13431     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13432      * loaded or when a record is removed. (defaults to false).
13433     */
13434     pruneModifiedRecords : false,
13435
13436     // private
13437     lastOptions : null,
13438
13439     /**
13440      * Add Records to the Store and fires the add event.
13441      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13442      */
13443     add : function(records){
13444         records = [].concat(records);
13445         for(var i = 0, len = records.length; i < len; i++){
13446             records[i].join(this);
13447         }
13448         var index = this.data.length;
13449         this.data.addAll(records);
13450         this.fireEvent("add", this, records, index);
13451     },
13452
13453     /**
13454      * Remove a Record from the Store and fires the remove event.
13455      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13456      */
13457     remove : function(record){
13458         var index = this.data.indexOf(record);
13459         this.data.removeAt(index);
13460  
13461         if(this.pruneModifiedRecords){
13462             this.modified.remove(record);
13463         }
13464         this.fireEvent("remove", this, record, index);
13465     },
13466
13467     /**
13468      * Remove all Records from the Store and fires the clear event.
13469      */
13470     removeAll : function(){
13471         this.data.clear();
13472         if(this.pruneModifiedRecords){
13473             this.modified = [];
13474         }
13475         this.fireEvent("clear", this);
13476     },
13477
13478     /**
13479      * Inserts Records to the Store at the given index and fires the add event.
13480      * @param {Number} index The start index at which to insert the passed Records.
13481      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13482      */
13483     insert : function(index, records){
13484         records = [].concat(records);
13485         for(var i = 0, len = records.length; i < len; i++){
13486             this.data.insert(index, records[i]);
13487             records[i].join(this);
13488         }
13489         this.fireEvent("add", this, records, index);
13490     },
13491
13492     /**
13493      * Get the index within the cache of the passed Record.
13494      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13495      * @return {Number} The index of the passed Record. Returns -1 if not found.
13496      */
13497     indexOf : function(record){
13498         return this.data.indexOf(record);
13499     },
13500
13501     /**
13502      * Get the index within the cache of the Record with the passed id.
13503      * @param {String} id The id of the Record to find.
13504      * @return {Number} The index of the Record. Returns -1 if not found.
13505      */
13506     indexOfId : function(id){
13507         return this.data.indexOfKey(id);
13508     },
13509
13510     /**
13511      * Get the Record with the specified id.
13512      * @param {String} id The id of the Record to find.
13513      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13514      */
13515     getById : function(id){
13516         return this.data.key(id);
13517     },
13518
13519     /**
13520      * Get the Record at the specified index.
13521      * @param {Number} index The index of the Record to find.
13522      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13523      */
13524     getAt : function(index){
13525         return this.data.itemAt(index);
13526     },
13527
13528     /**
13529      * Returns a range of Records between specified indices.
13530      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13531      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13532      * @return {Roo.data.Record[]} An array of Records
13533      */
13534     getRange : function(start, end){
13535         return this.data.getRange(start, end);
13536     },
13537
13538     // private
13539     storeOptions : function(o){
13540         o = Roo.apply({}, o);
13541         delete o.callback;
13542         delete o.scope;
13543         this.lastOptions = o;
13544     },
13545
13546     /**
13547      * Loads the Record cache from the configured Proxy using the configured Reader.
13548      * <p>
13549      * If using remote paging, then the first load call must specify the <em>start</em>
13550      * and <em>limit</em> properties in the options.params property to establish the initial
13551      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13552      * <p>
13553      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13554      * and this call will return before the new data has been loaded. Perform any post-processing
13555      * in a callback function, or in a "load" event handler.</strong>
13556      * <p>
13557      * @param {Object} options An object containing properties which control loading options:<ul>
13558      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13559      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13560      * passed the following arguments:<ul>
13561      * <li>r : Roo.data.Record[]</li>
13562      * <li>options: Options object from the load call</li>
13563      * <li>success: Boolean success indicator</li></ul></li>
13564      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13565      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13566      * </ul>
13567      */
13568     load : function(options){
13569         options = options || {};
13570         if(this.fireEvent("beforeload", this, options) !== false){
13571             this.storeOptions(options);
13572             var p = Roo.apply(options.params || {}, this.baseParams);
13573             // if meta was not loaded from remote source.. try requesting it.
13574             if (!this.reader.metaFromRemote) {
13575                 p._requestMeta = 1;
13576             }
13577             if(this.sortInfo && this.remoteSort){
13578                 var pn = this.paramNames;
13579                 p[pn["sort"]] = this.sortInfo.field;
13580                 p[pn["dir"]] = this.sortInfo.direction;
13581             }
13582             if (this.multiSort) {
13583                 var pn = this.paramNames;
13584                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13585             }
13586             
13587             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13588         }
13589     },
13590
13591     /**
13592      * Reloads the Record cache from the configured Proxy using the configured Reader and
13593      * the options from the last load operation performed.
13594      * @param {Object} options (optional) An object containing properties which may override the options
13595      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13596      * the most recently used options are reused).
13597      */
13598     reload : function(options){
13599         this.load(Roo.applyIf(options||{}, this.lastOptions));
13600     },
13601
13602     // private
13603     // Called as a callback by the Reader during a load operation.
13604     loadRecords : function(o, options, success){
13605         if(!o || success === false){
13606             if(success !== false){
13607                 this.fireEvent("load", this, [], options, o);
13608             }
13609             if(options.callback){
13610                 options.callback.call(options.scope || this, [], options, false);
13611             }
13612             return;
13613         }
13614         // if data returned failure - throw an exception.
13615         if (o.success === false) {
13616             // show a message if no listener is registered.
13617             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13618                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13619             }
13620             // loadmask wil be hooked into this..
13621             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13622             return;
13623         }
13624         var r = o.records, t = o.totalRecords || r.length;
13625         
13626         this.fireEvent("beforeloadadd", this, r, options, o);
13627         
13628         if(!options || options.add !== true){
13629             if(this.pruneModifiedRecords){
13630                 this.modified = [];
13631             }
13632             for(var i = 0, len = r.length; i < len; i++){
13633                 r[i].join(this);
13634             }
13635             if(this.snapshot){
13636                 this.data = this.snapshot;
13637                 delete this.snapshot;
13638             }
13639             this.data.clear();
13640             this.data.addAll(r);
13641             this.totalLength = t;
13642             this.applySort();
13643             this.fireEvent("datachanged", this);
13644         }else{
13645             this.totalLength = Math.max(t, this.data.length+r.length);
13646             this.add(r);
13647         }
13648         
13649         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13650                 
13651             var e = new Roo.data.Record({});
13652
13653             e.set(this.parent.displayField, this.parent.emptyTitle);
13654             e.set(this.parent.valueField, '');
13655
13656             this.insert(0, e);
13657         }
13658             
13659         this.fireEvent("load", this, r, options, o);
13660         if(options.callback){
13661             options.callback.call(options.scope || this, r, options, true);
13662         }
13663     },
13664
13665
13666     /**
13667      * Loads data from a passed data block. A Reader which understands the format of the data
13668      * must have been configured in the constructor.
13669      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13670      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13671      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13672      */
13673     loadData : function(o, append){
13674         var r = this.reader.readRecords(o);
13675         this.loadRecords(r, {add: append}, true);
13676     },
13677     
13678      /**
13679      * using 'cn' the nested child reader read the child array into it's child stores.
13680      * @param {Object} rec The record with a 'children array
13681      */
13682     loadDataFromChildren : function(rec)
13683     {
13684         this.loadData(this.reader.toLoadData(rec));
13685     },
13686     
13687
13688     /**
13689      * Gets the number of cached records.
13690      * <p>
13691      * <em>If using paging, this may not be the total size of the dataset. If the data object
13692      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13693      * the data set size</em>
13694      */
13695     getCount : function(){
13696         return this.data.length || 0;
13697     },
13698
13699     /**
13700      * Gets the total number of records in the dataset as returned by the server.
13701      * <p>
13702      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13703      * the dataset size</em>
13704      */
13705     getTotalCount : function(){
13706         return this.totalLength || 0;
13707     },
13708
13709     /**
13710      * Returns the sort state of the Store as an object with two properties:
13711      * <pre><code>
13712  field {String} The name of the field by which the Records are sorted
13713  direction {String} The sort order, "ASC" or "DESC"
13714      * </code></pre>
13715      */
13716     getSortState : function(){
13717         return this.sortInfo;
13718     },
13719
13720     // private
13721     applySort : function(){
13722         if(this.sortInfo && !this.remoteSort){
13723             var s = this.sortInfo, f = s.field;
13724             var st = this.fields.get(f).sortType;
13725             var fn = function(r1, r2){
13726                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13727                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13728             };
13729             this.data.sort(s.direction, fn);
13730             if(this.snapshot && this.snapshot != this.data){
13731                 this.snapshot.sort(s.direction, fn);
13732             }
13733         }
13734     },
13735
13736     /**
13737      * Sets the default sort column and order to be used by the next load operation.
13738      * @param {String} fieldName The name of the field to sort by.
13739      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13740      */
13741     setDefaultSort : function(field, dir){
13742         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13743     },
13744
13745     /**
13746      * Sort the Records.
13747      * If remote sorting is used, the sort is performed on the server, and the cache is
13748      * reloaded. If local sorting is used, the cache is sorted internally.
13749      * @param {String} fieldName The name of the field to sort by.
13750      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13751      */
13752     sort : function(fieldName, dir){
13753         var f = this.fields.get(fieldName);
13754         if(!dir){
13755             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13756             
13757             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13758                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13759             }else{
13760                 dir = f.sortDir;
13761             }
13762         }
13763         this.sortToggle[f.name] = dir;
13764         this.sortInfo = {field: f.name, direction: dir};
13765         if(!this.remoteSort){
13766             this.applySort();
13767             this.fireEvent("datachanged", this);
13768         }else{
13769             this.load(this.lastOptions);
13770         }
13771     },
13772
13773     /**
13774      * Calls the specified function for each of the Records in the cache.
13775      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13776      * Returning <em>false</em> aborts and exits the iteration.
13777      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13778      */
13779     each : function(fn, scope){
13780         this.data.each(fn, scope);
13781     },
13782
13783     /**
13784      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13785      * (e.g., during paging).
13786      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13787      */
13788     getModifiedRecords : function(){
13789         return this.modified;
13790     },
13791
13792     // private
13793     createFilterFn : function(property, value, anyMatch){
13794         if(!value.exec){ // not a regex
13795             value = String(value);
13796             if(value.length == 0){
13797                 return false;
13798             }
13799             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13800         }
13801         return function(r){
13802             return value.test(r.data[property]);
13803         };
13804     },
13805
13806     /**
13807      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13808      * @param {String} property A field on your records
13809      * @param {Number} start The record index to start at (defaults to 0)
13810      * @param {Number} end The last record index to include (defaults to length - 1)
13811      * @return {Number} The sum
13812      */
13813     sum : function(property, start, end){
13814         var rs = this.data.items, v = 0;
13815         start = start || 0;
13816         end = (end || end === 0) ? end : rs.length-1;
13817
13818         for(var i = start; i <= end; i++){
13819             v += (rs[i].data[property] || 0);
13820         }
13821         return v;
13822     },
13823
13824     /**
13825      * Filter the records by a specified property.
13826      * @param {String} field A field on your records
13827      * @param {String/RegExp} value Either a string that the field
13828      * should start with or a RegExp to test against the field
13829      * @param {Boolean} anyMatch True to match any part not just the beginning
13830      */
13831     filter : function(property, value, anyMatch){
13832         var fn = this.createFilterFn(property, value, anyMatch);
13833         return fn ? this.filterBy(fn) : this.clearFilter();
13834     },
13835
13836     /**
13837      * Filter by a function. The specified function will be called with each
13838      * record in this data source. If the function returns true the record is included,
13839      * otherwise it is filtered.
13840      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13841      * @param {Object} scope (optional) The scope of the function (defaults to this)
13842      */
13843     filterBy : function(fn, scope){
13844         this.snapshot = this.snapshot || this.data;
13845         this.data = this.queryBy(fn, scope||this);
13846         this.fireEvent("datachanged", this);
13847     },
13848
13849     /**
13850      * Query the records by a specified property.
13851      * @param {String} field A field on your records
13852      * @param {String/RegExp} value Either a string that the field
13853      * should start with or a RegExp to test against the field
13854      * @param {Boolean} anyMatch True to match any part not just the beginning
13855      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13856      */
13857     query : function(property, value, anyMatch){
13858         var fn = this.createFilterFn(property, value, anyMatch);
13859         return fn ? this.queryBy(fn) : this.data.clone();
13860     },
13861
13862     /**
13863      * Query by a function. The specified function will be called with each
13864      * record in this data source. If the function returns true the record is included
13865      * in the results.
13866      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13867      * @param {Object} scope (optional) The scope of the function (defaults to this)
13868       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13869      **/
13870     queryBy : function(fn, scope){
13871         var data = this.snapshot || this.data;
13872         return data.filterBy(fn, scope||this);
13873     },
13874
13875     /**
13876      * Collects unique values for a particular dataIndex from this store.
13877      * @param {String} dataIndex The property to collect
13878      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13879      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13880      * @return {Array} An array of the unique values
13881      **/
13882     collect : function(dataIndex, allowNull, bypassFilter){
13883         var d = (bypassFilter === true && this.snapshot) ?
13884                 this.snapshot.items : this.data.items;
13885         var v, sv, r = [], l = {};
13886         for(var i = 0, len = d.length; i < len; i++){
13887             v = d[i].data[dataIndex];
13888             sv = String(v);
13889             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13890                 l[sv] = true;
13891                 r[r.length] = v;
13892             }
13893         }
13894         return r;
13895     },
13896
13897     /**
13898      * Revert to a view of the Record cache with no filtering applied.
13899      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13900      */
13901     clearFilter : function(suppressEvent){
13902         if(this.snapshot && this.snapshot != this.data){
13903             this.data = this.snapshot;
13904             delete this.snapshot;
13905             if(suppressEvent !== true){
13906                 this.fireEvent("datachanged", this);
13907             }
13908         }
13909     },
13910
13911     // private
13912     afterEdit : function(record){
13913         if(this.modified.indexOf(record) == -1){
13914             this.modified.push(record);
13915         }
13916         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13917     },
13918     
13919     // private
13920     afterReject : function(record){
13921         this.modified.remove(record);
13922         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13923     },
13924
13925     // private
13926     afterCommit : function(record){
13927         this.modified.remove(record);
13928         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13929     },
13930
13931     /**
13932      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13933      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13934      */
13935     commitChanges : function(){
13936         var m = this.modified.slice(0);
13937         this.modified = [];
13938         for(var i = 0, len = m.length; i < len; i++){
13939             m[i].commit();
13940         }
13941     },
13942
13943     /**
13944      * Cancel outstanding changes on all changed records.
13945      */
13946     rejectChanges : function(){
13947         var m = this.modified.slice(0);
13948         this.modified = [];
13949         for(var i = 0, len = m.length; i < len; i++){
13950             m[i].reject();
13951         }
13952     },
13953
13954     onMetaChange : function(meta, rtype, o){
13955         this.recordType = rtype;
13956         this.fields = rtype.prototype.fields;
13957         delete this.snapshot;
13958         this.sortInfo = meta.sortInfo || this.sortInfo;
13959         this.modified = [];
13960         this.fireEvent('metachange', this, this.reader.meta);
13961     },
13962     
13963     moveIndex : function(data, type)
13964     {
13965         var index = this.indexOf(data);
13966         
13967         var newIndex = index + type;
13968         
13969         this.remove(data);
13970         
13971         this.insert(newIndex, data);
13972         
13973     }
13974 });/*
13975  * Based on:
13976  * Ext JS Library 1.1.1
13977  * Copyright(c) 2006-2007, Ext JS, LLC.
13978  *
13979  * Originally Released Under LGPL - original licence link has changed is not relivant.
13980  *
13981  * Fork - LGPL
13982  * <script type="text/javascript">
13983  */
13984
13985 /**
13986  * @class Roo.data.SimpleStore
13987  * @extends Roo.data.Store
13988  * Small helper class to make creating Stores from Array data easier.
13989  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13990  * @cfg {Array} fields An array of field definition objects, or field name strings.
13991  * @cfg {Object} an existing reader (eg. copied from another store)
13992  * @cfg {Array} data The multi-dimensional array of data
13993  * @constructor
13994  * @param {Object} config
13995  */
13996 Roo.data.SimpleStore = function(config)
13997 {
13998     Roo.data.SimpleStore.superclass.constructor.call(this, {
13999         isLocal : true,
14000         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
14001                 id: config.id
14002             },
14003             Roo.data.Record.create(config.fields)
14004         ),
14005         proxy : new Roo.data.MemoryProxy(config.data)
14006     });
14007     this.load();
14008 };
14009 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
14010  * Based on:
14011  * Ext JS Library 1.1.1
14012  * Copyright(c) 2006-2007, Ext JS, LLC.
14013  *
14014  * Originally Released Under LGPL - original licence link has changed is not relivant.
14015  *
14016  * Fork - LGPL
14017  * <script type="text/javascript">
14018  */
14019
14020 /**
14021 /**
14022  * @extends Roo.data.Store
14023  * @class Roo.data.JsonStore
14024  * Small helper class to make creating Stores for JSON data easier. <br/>
14025 <pre><code>
14026 var store = new Roo.data.JsonStore({
14027     url: 'get-images.php',
14028     root: 'images',
14029     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
14030 });
14031 </code></pre>
14032  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
14033  * JsonReader and HttpProxy (unless inline data is provided).</b>
14034  * @cfg {Array} fields An array of field definition objects, or field name strings.
14035  * @constructor
14036  * @param {Object} config
14037  */
14038 Roo.data.JsonStore = function(c){
14039     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
14040         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
14041         reader: new Roo.data.JsonReader(c, c.fields)
14042     }));
14043 };
14044 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
14045  * Based on:
14046  * Ext JS Library 1.1.1
14047  * Copyright(c) 2006-2007, Ext JS, LLC.
14048  *
14049  * Originally Released Under LGPL - original licence link has changed is not relivant.
14050  *
14051  * Fork - LGPL
14052  * <script type="text/javascript">
14053  */
14054
14055  
14056 Roo.data.Field = function(config){
14057     if(typeof config == "string"){
14058         config = {name: config};
14059     }
14060     Roo.apply(this, config);
14061     
14062     if(!this.type){
14063         this.type = "auto";
14064     }
14065     
14066     var st = Roo.data.SortTypes;
14067     // named sortTypes are supported, here we look them up
14068     if(typeof this.sortType == "string"){
14069         this.sortType = st[this.sortType];
14070     }
14071     
14072     // set default sortType for strings and dates
14073     if(!this.sortType){
14074         switch(this.type){
14075             case "string":
14076                 this.sortType = st.asUCString;
14077                 break;
14078             case "date":
14079                 this.sortType = st.asDate;
14080                 break;
14081             default:
14082                 this.sortType = st.none;
14083         }
14084     }
14085
14086     // define once
14087     var stripRe = /[\$,%]/g;
14088
14089     // prebuilt conversion function for this field, instead of
14090     // switching every time we're reading a value
14091     if(!this.convert){
14092         var cv, dateFormat = this.dateFormat;
14093         switch(this.type){
14094             case "":
14095             case "auto":
14096             case undefined:
14097                 cv = function(v){ return v; };
14098                 break;
14099             case "string":
14100                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14101                 break;
14102             case "int":
14103                 cv = function(v){
14104                     return v !== undefined && v !== null && v !== '' ?
14105                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14106                     };
14107                 break;
14108             case "float":
14109                 cv = function(v){
14110                     return v !== undefined && v !== null && v !== '' ?
14111                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14112                     };
14113                 break;
14114             case "bool":
14115             case "boolean":
14116                 cv = function(v){ return v === true || v === "true" || v == 1; };
14117                 break;
14118             case "date":
14119                 cv = function(v){
14120                     if(!v){
14121                         return '';
14122                     }
14123                     if(v instanceof Date){
14124                         return v;
14125                     }
14126                     if(dateFormat){
14127                         if(dateFormat == "timestamp"){
14128                             return new Date(v*1000);
14129                         }
14130                         return Date.parseDate(v, dateFormat);
14131                     }
14132                     var parsed = Date.parse(v);
14133                     return parsed ? new Date(parsed) : null;
14134                 };
14135              break;
14136             
14137         }
14138         this.convert = cv;
14139     }
14140 };
14141
14142 Roo.data.Field.prototype = {
14143     dateFormat: null,
14144     defaultValue: "",
14145     mapping: null,
14146     sortType : null,
14147     sortDir : "ASC"
14148 };/*
14149  * Based on:
14150  * Ext JS Library 1.1.1
14151  * Copyright(c) 2006-2007, Ext JS, LLC.
14152  *
14153  * Originally Released Under LGPL - original licence link has changed is not relivant.
14154  *
14155  * Fork - LGPL
14156  * <script type="text/javascript">
14157  */
14158  
14159 // Base class for reading structured data from a data source.  This class is intended to be
14160 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14161
14162 /**
14163  * @class Roo.data.DataReader
14164  * Base class for reading structured data from a data source.  This class is intended to be
14165  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14166  */
14167
14168 Roo.data.DataReader = function(meta, recordType){
14169     
14170     this.meta = meta;
14171     
14172     this.recordType = recordType instanceof Array ? 
14173         Roo.data.Record.create(recordType) : recordType;
14174 };
14175
14176 Roo.data.DataReader.prototype = {
14177     
14178     
14179     readerType : 'Data',
14180      /**
14181      * Create an empty record
14182      * @param {Object} data (optional) - overlay some values
14183      * @return {Roo.data.Record} record created.
14184      */
14185     newRow :  function(d) {
14186         var da =  {};
14187         this.recordType.prototype.fields.each(function(c) {
14188             switch( c.type) {
14189                 case 'int' : da[c.name] = 0; break;
14190                 case 'date' : da[c.name] = new Date(); break;
14191                 case 'float' : da[c.name] = 0.0; break;
14192                 case 'boolean' : da[c.name] = false; break;
14193                 default : da[c.name] = ""; break;
14194             }
14195             
14196         });
14197         return new this.recordType(Roo.apply(da, d));
14198     }
14199     
14200     
14201 };/*
14202  * Based on:
14203  * Ext JS Library 1.1.1
14204  * Copyright(c) 2006-2007, Ext JS, LLC.
14205  *
14206  * Originally Released Under LGPL - original licence link has changed is not relivant.
14207  *
14208  * Fork - LGPL
14209  * <script type="text/javascript">
14210  */
14211
14212 /**
14213  * @class Roo.data.DataProxy
14214  * @extends Roo.data.Observable
14215  * This class is an abstract base class for implementations which provide retrieval of
14216  * unformatted data objects.<br>
14217  * <p>
14218  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14219  * (of the appropriate type which knows how to parse the data object) to provide a block of
14220  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14221  * <p>
14222  * Custom implementations must implement the load method as described in
14223  * {@link Roo.data.HttpProxy#load}.
14224  */
14225 Roo.data.DataProxy = function(){
14226     this.addEvents({
14227         /**
14228          * @event beforeload
14229          * Fires before a network request is made to retrieve a data object.
14230          * @param {Object} This DataProxy object.
14231          * @param {Object} params The params parameter to the load function.
14232          */
14233         beforeload : true,
14234         /**
14235          * @event load
14236          * Fires before the load method's callback is called.
14237          * @param {Object} This DataProxy object.
14238          * @param {Object} o The data object.
14239          * @param {Object} arg The callback argument object passed to the load function.
14240          */
14241         load : true,
14242         /**
14243          * @event loadexception
14244          * Fires if an Exception occurs during data retrieval.
14245          * @param {Object} This DataProxy object.
14246          * @param {Object} o The data object.
14247          * @param {Object} arg The callback argument object passed to the load function.
14248          * @param {Object} e The Exception.
14249          */
14250         loadexception : true
14251     });
14252     Roo.data.DataProxy.superclass.constructor.call(this);
14253 };
14254
14255 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14256
14257     /**
14258      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14259      */
14260 /*
14261  * Based on:
14262  * Ext JS Library 1.1.1
14263  * Copyright(c) 2006-2007, Ext JS, LLC.
14264  *
14265  * Originally Released Under LGPL - original licence link has changed is not relivant.
14266  *
14267  * Fork - LGPL
14268  * <script type="text/javascript">
14269  */
14270 /**
14271  * @class Roo.data.MemoryProxy
14272  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14273  * to the Reader when its load method is called.
14274  * @constructor
14275  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14276  */
14277 Roo.data.MemoryProxy = function(data){
14278     if (data.data) {
14279         data = data.data;
14280     }
14281     Roo.data.MemoryProxy.superclass.constructor.call(this);
14282     this.data = data;
14283 };
14284
14285 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14286     
14287     /**
14288      * Load data from the requested source (in this case an in-memory
14289      * data object passed to the constructor), read the data object into
14290      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14291      * process that block using the passed callback.
14292      * @param {Object} params This parameter is not used by the MemoryProxy class.
14293      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14294      * object into a block of Roo.data.Records.
14295      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14296      * The function must be passed <ul>
14297      * <li>The Record block object</li>
14298      * <li>The "arg" argument from the load function</li>
14299      * <li>A boolean success indicator</li>
14300      * </ul>
14301      * @param {Object} scope The scope in which to call the callback
14302      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14303      */
14304     load : function(params, reader, callback, scope, arg){
14305         params = params || {};
14306         var result;
14307         try {
14308             result = reader.readRecords(params.data ? params.data :this.data);
14309         }catch(e){
14310             this.fireEvent("loadexception", this, arg, null, e);
14311             callback.call(scope, null, arg, false);
14312             return;
14313         }
14314         callback.call(scope, result, arg, true);
14315     },
14316     
14317     // private
14318     update : function(params, records){
14319         
14320     }
14321 });/*
14322  * Based on:
14323  * Ext JS Library 1.1.1
14324  * Copyright(c) 2006-2007, Ext JS, LLC.
14325  *
14326  * Originally Released Under LGPL - original licence link has changed is not relivant.
14327  *
14328  * Fork - LGPL
14329  * <script type="text/javascript">
14330  */
14331 /**
14332  * @class Roo.data.HttpProxy
14333  * @extends Roo.data.DataProxy
14334  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14335  * configured to reference a certain URL.<br><br>
14336  * <p>
14337  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14338  * from which the running page was served.<br><br>
14339  * <p>
14340  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14341  * <p>
14342  * Be aware that to enable the browser to parse an XML document, the server must set
14343  * the Content-Type header in the HTTP response to "text/xml".
14344  * @constructor
14345  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14346  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14347  * will be used to make the request.
14348  */
14349 Roo.data.HttpProxy = function(conn){
14350     Roo.data.HttpProxy.superclass.constructor.call(this);
14351     // is conn a conn config or a real conn?
14352     this.conn = conn;
14353     this.useAjax = !conn || !conn.events;
14354   
14355 };
14356
14357 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14358     // thse are take from connection...
14359     
14360     /**
14361      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14362      */
14363     /**
14364      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14365      * extra parameters to each request made by this object. (defaults to undefined)
14366      */
14367     /**
14368      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14369      *  to each request made by this object. (defaults to undefined)
14370      */
14371     /**
14372      * @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)
14373      */
14374     /**
14375      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14376      */
14377      /**
14378      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14379      * @type Boolean
14380      */
14381   
14382
14383     /**
14384      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14385      * @type Boolean
14386      */
14387     /**
14388      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14389      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14390      * a finer-grained basis than the DataProxy events.
14391      */
14392     getConnection : function(){
14393         return this.useAjax ? Roo.Ajax : this.conn;
14394     },
14395
14396     /**
14397      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14398      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14399      * process that block using the passed callback.
14400      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14401      * for the request to the remote server.
14402      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14403      * object into a block of Roo.data.Records.
14404      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14405      * The function must be passed <ul>
14406      * <li>The Record block object</li>
14407      * <li>The "arg" argument from the load function</li>
14408      * <li>A boolean success indicator</li>
14409      * </ul>
14410      * @param {Object} scope The scope in which to call the callback
14411      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14412      */
14413     load : function(params, reader, callback, scope, arg){
14414         if(this.fireEvent("beforeload", this, params) !== false){
14415             var  o = {
14416                 params : params || {},
14417                 request: {
14418                     callback : callback,
14419                     scope : scope,
14420                     arg : arg
14421                 },
14422                 reader: reader,
14423                 callback : this.loadResponse,
14424                 scope: this
14425             };
14426             if(this.useAjax){
14427                 Roo.applyIf(o, this.conn);
14428                 if(this.activeRequest){
14429                     Roo.Ajax.abort(this.activeRequest);
14430                 }
14431                 this.activeRequest = Roo.Ajax.request(o);
14432             }else{
14433                 this.conn.request(o);
14434             }
14435         }else{
14436             callback.call(scope||this, null, arg, false);
14437         }
14438     },
14439
14440     // private
14441     loadResponse : function(o, success, response){
14442         delete this.activeRequest;
14443         if(!success){
14444             this.fireEvent("loadexception", this, o, response);
14445             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14446             return;
14447         }
14448         var result;
14449         try {
14450             result = o.reader.read(response);
14451         }catch(e){
14452             this.fireEvent("loadexception", this, o, response, e);
14453             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14454             return;
14455         }
14456         
14457         this.fireEvent("load", this, o, o.request.arg);
14458         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14459     },
14460
14461     // private
14462     update : function(dataSet){
14463
14464     },
14465
14466     // private
14467     updateResponse : function(dataSet){
14468
14469     }
14470 });/*
14471  * Based on:
14472  * Ext JS Library 1.1.1
14473  * Copyright(c) 2006-2007, Ext JS, LLC.
14474  *
14475  * Originally Released Under LGPL - original licence link has changed is not relivant.
14476  *
14477  * Fork - LGPL
14478  * <script type="text/javascript">
14479  */
14480
14481 /**
14482  * @class Roo.data.ScriptTagProxy
14483  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14484  * other than the originating domain of the running page.<br><br>
14485  * <p>
14486  * <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
14487  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14488  * <p>
14489  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14490  * source code that is used as the source inside a &lt;script> tag.<br><br>
14491  * <p>
14492  * In order for the browser to process the returned data, the server must wrap the data object
14493  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14494  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14495  * depending on whether the callback name was passed:
14496  * <p>
14497  * <pre><code>
14498 boolean scriptTag = false;
14499 String cb = request.getParameter("callback");
14500 if (cb != null) {
14501     scriptTag = true;
14502     response.setContentType("text/javascript");
14503 } else {
14504     response.setContentType("application/x-json");
14505 }
14506 Writer out = response.getWriter();
14507 if (scriptTag) {
14508     out.write(cb + "(");
14509 }
14510 out.print(dataBlock.toJsonString());
14511 if (scriptTag) {
14512     out.write(");");
14513 }
14514 </pre></code>
14515  *
14516  * @constructor
14517  * @param {Object} config A configuration object.
14518  */
14519 Roo.data.ScriptTagProxy = function(config){
14520     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14521     Roo.apply(this, config);
14522     this.head = document.getElementsByTagName("head")[0];
14523 };
14524
14525 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14526
14527 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14528     /**
14529      * @cfg {String} url The URL from which to request the data object.
14530      */
14531     /**
14532      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14533      */
14534     timeout : 30000,
14535     /**
14536      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14537      * the server the name of the callback function set up by the load call to process the returned data object.
14538      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14539      * javascript output which calls this named function passing the data object as its only parameter.
14540      */
14541     callbackParam : "callback",
14542     /**
14543      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14544      * name to the request.
14545      */
14546     nocache : true,
14547
14548     /**
14549      * Load data from the configured URL, read the data object into
14550      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14551      * process that block using the passed callback.
14552      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14553      * for the request to the remote server.
14554      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14555      * object into a block of Roo.data.Records.
14556      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14557      * The function must be passed <ul>
14558      * <li>The Record block object</li>
14559      * <li>The "arg" argument from the load function</li>
14560      * <li>A boolean success indicator</li>
14561      * </ul>
14562      * @param {Object} scope The scope in which to call the callback
14563      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14564      */
14565     load : function(params, reader, callback, scope, arg){
14566         if(this.fireEvent("beforeload", this, params) !== false){
14567
14568             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14569
14570             var url = this.url;
14571             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14572             if(this.nocache){
14573                 url += "&_dc=" + (new Date().getTime());
14574             }
14575             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14576             var trans = {
14577                 id : transId,
14578                 cb : "stcCallback"+transId,
14579                 scriptId : "stcScript"+transId,
14580                 params : params,
14581                 arg : arg,
14582                 url : url,
14583                 callback : callback,
14584                 scope : scope,
14585                 reader : reader
14586             };
14587             var conn = this;
14588
14589             window[trans.cb] = function(o){
14590                 conn.handleResponse(o, trans);
14591             };
14592
14593             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14594
14595             if(this.autoAbort !== false){
14596                 this.abort();
14597             }
14598
14599             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14600
14601             var script = document.createElement("script");
14602             script.setAttribute("src", url);
14603             script.setAttribute("type", "text/javascript");
14604             script.setAttribute("id", trans.scriptId);
14605             this.head.appendChild(script);
14606
14607             this.trans = trans;
14608         }else{
14609             callback.call(scope||this, null, arg, false);
14610         }
14611     },
14612
14613     // private
14614     isLoading : function(){
14615         return this.trans ? true : false;
14616     },
14617
14618     /**
14619      * Abort the current server request.
14620      */
14621     abort : function(){
14622         if(this.isLoading()){
14623             this.destroyTrans(this.trans);
14624         }
14625     },
14626
14627     // private
14628     destroyTrans : function(trans, isLoaded){
14629         this.head.removeChild(document.getElementById(trans.scriptId));
14630         clearTimeout(trans.timeoutId);
14631         if(isLoaded){
14632             window[trans.cb] = undefined;
14633             try{
14634                 delete window[trans.cb];
14635             }catch(e){}
14636         }else{
14637             // if hasn't been loaded, wait for load to remove it to prevent script error
14638             window[trans.cb] = function(){
14639                 window[trans.cb] = undefined;
14640                 try{
14641                     delete window[trans.cb];
14642                 }catch(e){}
14643             };
14644         }
14645     },
14646
14647     // private
14648     handleResponse : function(o, trans){
14649         this.trans = false;
14650         this.destroyTrans(trans, true);
14651         var result;
14652         try {
14653             result = trans.reader.readRecords(o);
14654         }catch(e){
14655             this.fireEvent("loadexception", this, o, trans.arg, e);
14656             trans.callback.call(trans.scope||window, null, trans.arg, false);
14657             return;
14658         }
14659         this.fireEvent("load", this, o, trans.arg);
14660         trans.callback.call(trans.scope||window, result, trans.arg, true);
14661     },
14662
14663     // private
14664     handleFailure : function(trans){
14665         this.trans = false;
14666         this.destroyTrans(trans, false);
14667         this.fireEvent("loadexception", this, null, trans.arg);
14668         trans.callback.call(trans.scope||window, null, trans.arg, false);
14669     }
14670 });/*
14671  * Based on:
14672  * Ext JS Library 1.1.1
14673  * Copyright(c) 2006-2007, Ext JS, LLC.
14674  *
14675  * Originally Released Under LGPL - original licence link has changed is not relivant.
14676  *
14677  * Fork - LGPL
14678  * <script type="text/javascript">
14679  */
14680
14681 /**
14682  * @class Roo.data.JsonReader
14683  * @extends Roo.data.DataReader
14684  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14685  * based on mappings in a provided Roo.data.Record constructor.
14686  * 
14687  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14688  * in the reply previously. 
14689  * 
14690  * <p>
14691  * Example code:
14692  * <pre><code>
14693 var RecordDef = Roo.data.Record.create([
14694     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14695     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14696 ]);
14697 var myReader = new Roo.data.JsonReader({
14698     totalProperty: "results",    // The property which contains the total dataset size (optional)
14699     root: "rows",                // The property which contains an Array of row objects
14700     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14701 }, RecordDef);
14702 </code></pre>
14703  * <p>
14704  * This would consume a JSON file like this:
14705  * <pre><code>
14706 { 'results': 2, 'rows': [
14707     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14708     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14709 }
14710 </code></pre>
14711  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14712  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14713  * paged from the remote server.
14714  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14715  * @cfg {String} root name of the property which contains the Array of row objects.
14716  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14717  * @cfg {Array} fields Array of field definition objects
14718  * @constructor
14719  * Create a new JsonReader
14720  * @param {Object} meta Metadata configuration options
14721  * @param {Object} recordType Either an Array of field definition objects,
14722  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14723  */
14724 Roo.data.JsonReader = function(meta, recordType){
14725     
14726     meta = meta || {};
14727     // set some defaults:
14728     Roo.applyIf(meta, {
14729         totalProperty: 'total',
14730         successProperty : 'success',
14731         root : 'data',
14732         id : 'id'
14733     });
14734     
14735     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14736 };
14737 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14738     
14739     readerType : 'Json',
14740     
14741     /**
14742      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14743      * Used by Store query builder to append _requestMeta to params.
14744      * 
14745      */
14746     metaFromRemote : false,
14747     /**
14748      * This method is only used by a DataProxy which has retrieved data from a remote server.
14749      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14750      * @return {Object} data A data block which is used by an Roo.data.Store object as
14751      * a cache of Roo.data.Records.
14752      */
14753     read : function(response){
14754         var json = response.responseText;
14755        
14756         var o = /* eval:var:o */ eval("("+json+")");
14757         if(!o) {
14758             throw {message: "JsonReader.read: Json object not found"};
14759         }
14760         
14761         if(o.metaData){
14762             
14763             delete this.ef;
14764             this.metaFromRemote = true;
14765             this.meta = o.metaData;
14766             this.recordType = Roo.data.Record.create(o.metaData.fields);
14767             this.onMetaChange(this.meta, this.recordType, o);
14768         }
14769         return this.readRecords(o);
14770     },
14771
14772     // private function a store will implement
14773     onMetaChange : function(meta, recordType, o){
14774
14775     },
14776
14777     /**
14778          * @ignore
14779          */
14780     simpleAccess: function(obj, subsc) {
14781         return obj[subsc];
14782     },
14783
14784         /**
14785          * @ignore
14786          */
14787     getJsonAccessor: function(){
14788         var re = /[\[\.]/;
14789         return function(expr) {
14790             try {
14791                 return(re.test(expr))
14792                     ? new Function("obj", "return obj." + expr)
14793                     : function(obj){
14794                         return obj[expr];
14795                     };
14796             } catch(e){}
14797             return Roo.emptyFn;
14798         };
14799     }(),
14800
14801     /**
14802      * Create a data block containing Roo.data.Records from an XML document.
14803      * @param {Object} o An object which contains an Array of row objects in the property specified
14804      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14805      * which contains the total size of the dataset.
14806      * @return {Object} data A data block which is used by an Roo.data.Store object as
14807      * a cache of Roo.data.Records.
14808      */
14809     readRecords : function(o){
14810         /**
14811          * After any data loads, the raw JSON data is available for further custom processing.
14812          * @type Object
14813          */
14814         this.o = o;
14815         var s = this.meta, Record = this.recordType,
14816             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14817
14818 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14819         if (!this.ef) {
14820             if(s.totalProperty) {
14821                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14822                 }
14823                 if(s.successProperty) {
14824                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14825                 }
14826                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14827                 if (s.id) {
14828                         var g = this.getJsonAccessor(s.id);
14829                         this.getId = function(rec) {
14830                                 var r = g(rec);  
14831                                 return (r === undefined || r === "") ? null : r;
14832                         };
14833                 } else {
14834                         this.getId = function(){return null;};
14835                 }
14836             this.ef = [];
14837             for(var jj = 0; jj < fl; jj++){
14838                 f = fi[jj];
14839                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14840                 this.ef[jj] = this.getJsonAccessor(map);
14841             }
14842         }
14843
14844         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14845         if(s.totalProperty){
14846             var vt = parseInt(this.getTotal(o), 10);
14847             if(!isNaN(vt)){
14848                 totalRecords = vt;
14849             }
14850         }
14851         if(s.successProperty){
14852             var vs = this.getSuccess(o);
14853             if(vs === false || vs === 'false'){
14854                 success = false;
14855             }
14856         }
14857         var records = [];
14858         for(var i = 0; i < c; i++){
14859                 var n = root[i];
14860             var values = {};
14861             var id = this.getId(n);
14862             for(var j = 0; j < fl; j++){
14863                 f = fi[j];
14864             var v = this.ef[j](n);
14865             if (!f.convert) {
14866                 Roo.log('missing convert for ' + f.name);
14867                 Roo.log(f);
14868                 continue;
14869             }
14870             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14871             }
14872             var record = new Record(values, id);
14873             record.json = n;
14874             records[i] = record;
14875         }
14876         return {
14877             raw : o,
14878             success : success,
14879             records : records,
14880             totalRecords : totalRecords
14881         };
14882     },
14883     // used when loading children.. @see loadDataFromChildren
14884     toLoadData: function(rec)
14885     {
14886         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14887         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14888         return { data : data, total : data.length };
14889         
14890     }
14891 });/*
14892  * Based on:
14893  * Ext JS Library 1.1.1
14894  * Copyright(c) 2006-2007, Ext JS, LLC.
14895  *
14896  * Originally Released Under LGPL - original licence link has changed is not relivant.
14897  *
14898  * Fork - LGPL
14899  * <script type="text/javascript">
14900  */
14901
14902 /**
14903  * @class Roo.data.ArrayReader
14904  * @extends Roo.data.DataReader
14905  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14906  * Each element of that Array represents a row of data fields. The
14907  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14908  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14909  * <p>
14910  * Example code:.
14911  * <pre><code>
14912 var RecordDef = Roo.data.Record.create([
14913     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14914     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14915 ]);
14916 var myReader = new Roo.data.ArrayReader({
14917     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14918 }, RecordDef);
14919 </code></pre>
14920  * <p>
14921  * This would consume an Array like this:
14922  * <pre><code>
14923 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14924   </code></pre>
14925  
14926  * @constructor
14927  * Create a new JsonReader
14928  * @param {Object} meta Metadata configuration options.
14929  * @param {Object|Array} recordType Either an Array of field definition objects
14930  * 
14931  * @cfg {Array} fields Array of field definition objects
14932  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14933  * as specified to {@link Roo.data.Record#create},
14934  * or an {@link Roo.data.Record} object
14935  *
14936  * 
14937  * created using {@link Roo.data.Record#create}.
14938  */
14939 Roo.data.ArrayReader = function(meta, recordType)
14940 {    
14941     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14942 };
14943
14944 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14945     
14946       /**
14947      * Create a data block containing Roo.data.Records from an XML document.
14948      * @param {Object} o An Array of row objects which represents the dataset.
14949      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14950      * a cache of Roo.data.Records.
14951      */
14952     readRecords : function(o)
14953     {
14954         var sid = this.meta ? this.meta.id : null;
14955         var recordType = this.recordType, fields = recordType.prototype.fields;
14956         var records = [];
14957         var root = o;
14958         for(var i = 0; i < root.length; i++){
14959                 var n = root[i];
14960             var values = {};
14961             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14962             for(var j = 0, jlen = fields.length; j < jlen; j++){
14963                 var f = fields.items[j];
14964                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14965                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14966                 v = f.convert(v);
14967                 values[f.name] = v;
14968             }
14969             var record = new recordType(values, id);
14970             record.json = n;
14971             records[records.length] = record;
14972         }
14973         return {
14974             records : records,
14975             totalRecords : records.length
14976         };
14977     },
14978     // used when loading children.. @see loadDataFromChildren
14979     toLoadData: function(rec)
14980     {
14981         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14982         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14983         
14984     }
14985     
14986     
14987 });/*
14988  * - LGPL
14989  * * 
14990  */
14991
14992 /**
14993  * @class Roo.bootstrap.ComboBox
14994  * @extends Roo.bootstrap.TriggerField
14995  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14996  * @cfg {Boolean} append (true|false) default false
14997  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14998  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14999  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
15000  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
15001  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
15002  * @cfg {Boolean} animate default true
15003  * @cfg {Boolean} emptyResultText only for touch device
15004  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
15005  * @cfg {String} emptyTitle default ''
15006  * @cfg {Number} width fixed with? experimental
15007  * @constructor
15008  * Create a new ComboBox.
15009  * @param {Object} config Configuration options
15010  */
15011 Roo.bootstrap.ComboBox = function(config){
15012     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
15013     this.addEvents({
15014         /**
15015          * @event expand
15016          * Fires when the dropdown list is expanded
15017         * @param {Roo.bootstrap.ComboBox} combo This combo box
15018         */
15019         'expand' : true,
15020         /**
15021          * @event collapse
15022          * Fires when the dropdown list is collapsed
15023         * @param {Roo.bootstrap.ComboBox} combo This combo box
15024         */
15025         'collapse' : true,
15026         /**
15027          * @event beforeselect
15028          * Fires before a list item is selected. Return false to cancel the selection.
15029         * @param {Roo.bootstrap.ComboBox} combo This combo box
15030         * @param {Roo.data.Record} record The data record returned from the underlying store
15031         * @param {Number} index The index of the selected item in the dropdown list
15032         */
15033         'beforeselect' : true,
15034         /**
15035          * @event select
15036          * Fires when a list item is selected
15037         * @param {Roo.bootstrap.ComboBox} combo This combo box
15038         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
15039         * @param {Number} index The index of the selected item in the dropdown list
15040         */
15041         'select' : true,
15042         /**
15043          * @event beforequery
15044          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
15045          * The event object passed has these properties:
15046         * @param {Roo.bootstrap.ComboBox} combo This combo box
15047         * @param {String} query The query
15048         * @param {Boolean} forceAll true to force "all" query
15049         * @param {Boolean} cancel true to cancel the query
15050         * @param {Object} e The query event object
15051         */
15052         'beforequery': true,
15053          /**
15054          * @event add
15055          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15056         * @param {Roo.bootstrap.ComboBox} combo This combo box
15057         */
15058         'add' : true,
15059         /**
15060          * @event edit
15061          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15062         * @param {Roo.bootstrap.ComboBox} combo This combo box
15063         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15064         */
15065         'edit' : true,
15066         /**
15067          * @event remove
15068          * Fires when the remove value from the combobox array
15069         * @param {Roo.bootstrap.ComboBox} combo This combo box
15070         */
15071         'remove' : true,
15072         /**
15073          * @event afterremove
15074          * Fires when the remove value from the combobox array
15075         * @param {Roo.bootstrap.ComboBox} combo This combo box
15076         */
15077         'afterremove' : true,
15078         /**
15079          * @event specialfilter
15080          * Fires when specialfilter
15081             * @param {Roo.bootstrap.ComboBox} combo This combo box
15082             */
15083         'specialfilter' : true,
15084         /**
15085          * @event tick
15086          * Fires when tick the element
15087             * @param {Roo.bootstrap.ComboBox} combo This combo box
15088             */
15089         'tick' : true,
15090         /**
15091          * @event touchviewdisplay
15092          * Fires when touch view require special display (default is using displayField)
15093             * @param {Roo.bootstrap.ComboBox} combo This combo box
15094             * @param {Object} cfg set html .
15095             */
15096         'touchviewdisplay' : true
15097         
15098     });
15099     
15100     this.item = [];
15101     this.tickItems = [];
15102     
15103     this.selectedIndex = -1;
15104     if(this.mode == 'local'){
15105         if(config.queryDelay === undefined){
15106             this.queryDelay = 10;
15107         }
15108         if(config.minChars === undefined){
15109             this.minChars = 0;
15110         }
15111     }
15112 };
15113
15114 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15115      
15116     /**
15117      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15118      * rendering into an Roo.Editor, defaults to false)
15119      */
15120     /**
15121      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15122      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15123      */
15124     /**
15125      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15126      */
15127     /**
15128      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15129      * the dropdown list (defaults to undefined, with no header element)
15130      */
15131
15132      /**
15133      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15134      */
15135      
15136      /**
15137      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15138      */
15139     listWidth: undefined,
15140     /**
15141      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15142      * mode = 'remote' or 'text' if mode = 'local')
15143      */
15144     displayField: undefined,
15145     
15146     /**
15147      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15148      * mode = 'remote' or 'value' if mode = 'local'). 
15149      * Note: use of a valueField requires the user make a selection
15150      * in order for a value to be mapped.
15151      */
15152     valueField: undefined,
15153     /**
15154      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15155      */
15156     modalTitle : '',
15157     
15158     /**
15159      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15160      * field's data value (defaults to the underlying DOM element's name)
15161      */
15162     hiddenName: undefined,
15163     /**
15164      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15165      */
15166     listClass: '',
15167     /**
15168      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15169      */
15170     selectedClass: 'active',
15171     
15172     /**
15173      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15174      */
15175     shadow:'sides',
15176     /**
15177      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15178      * anchor positions (defaults to 'tl-bl')
15179      */
15180     listAlign: 'tl-bl?',
15181     /**
15182      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15183      */
15184     maxHeight: 300,
15185     /**
15186      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15187      * query specified by the allQuery config option (defaults to 'query')
15188      */
15189     triggerAction: 'query',
15190     /**
15191      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15192      * (defaults to 4, does not apply if editable = false)
15193      */
15194     minChars : 4,
15195     /**
15196      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15197      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15198      */
15199     typeAhead: false,
15200     /**
15201      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15202      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15203      */
15204     queryDelay: 500,
15205     /**
15206      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15207      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15208      */
15209     pageSize: 0,
15210     /**
15211      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15212      * when editable = true (defaults to false)
15213      */
15214     selectOnFocus:false,
15215     /**
15216      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15217      */
15218     queryParam: 'query',
15219     /**
15220      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15221      * when mode = 'remote' (defaults to 'Loading...')
15222      */
15223     loadingText: 'Loading...',
15224     /**
15225      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15226      */
15227     resizable: false,
15228     /**
15229      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15230      */
15231     handleHeight : 8,
15232     /**
15233      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15234      * traditional select (defaults to true)
15235      */
15236     editable: true,
15237     /**
15238      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15239      */
15240     allQuery: '',
15241     /**
15242      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15243      */
15244     mode: 'remote',
15245     /**
15246      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15247      * listWidth has a higher value)
15248      */
15249     minListWidth : 70,
15250     /**
15251      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15252      * allow the user to set arbitrary text into the field (defaults to false)
15253      */
15254     forceSelection:false,
15255     /**
15256      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15257      * if typeAhead = true (defaults to 250)
15258      */
15259     typeAheadDelay : 250,
15260     /**
15261      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15262      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15263      */
15264     valueNotFoundText : undefined,
15265     /**
15266      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15267      */
15268     blockFocus : false,
15269     
15270     /**
15271      * @cfg {Boolean} disableClear Disable showing of clear button.
15272      */
15273     disableClear : false,
15274     /**
15275      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15276      */
15277     alwaysQuery : false,
15278     
15279     /**
15280      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15281      */
15282     multiple : false,
15283     
15284     /**
15285      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15286      */
15287     invalidClass : "has-warning",
15288     
15289     /**
15290      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15291      */
15292     validClass : "has-success",
15293     
15294     /**
15295      * @cfg {Boolean} specialFilter (true|false) special filter default false
15296      */
15297     specialFilter : false,
15298     
15299     /**
15300      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15301      */
15302     mobileTouchView : true,
15303     
15304     /**
15305      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15306      */
15307     useNativeIOS : false,
15308     
15309     /**
15310      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15311      */
15312     mobile_restrict_height : false,
15313     
15314     ios_options : false,
15315     
15316     //private
15317     addicon : false,
15318     editicon: false,
15319     
15320     page: 0,
15321     hasQuery: false,
15322     append: false,
15323     loadNext: false,
15324     autoFocus : true,
15325     tickable : false,
15326     btnPosition : 'right',
15327     triggerList : true,
15328     showToggleBtn : true,
15329     animate : true,
15330     emptyResultText: 'Empty',
15331     triggerText : 'Select',
15332     emptyTitle : '',
15333     width : false,
15334     
15335     // element that contains real text value.. (when hidden is used..)
15336     
15337     getAutoCreate : function()
15338     {   
15339         var cfg = false;
15340         //render
15341         /*
15342          * Render classic select for iso
15343          */
15344         
15345         if(Roo.isIOS && this.useNativeIOS){
15346             cfg = this.getAutoCreateNativeIOS();
15347             return cfg;
15348         }
15349         
15350         /*
15351          * Touch Devices
15352          */
15353         
15354         if(Roo.isTouch && this.mobileTouchView){
15355             cfg = this.getAutoCreateTouchView();
15356             return cfg;;
15357         }
15358         
15359         /*
15360          *  Normal ComboBox
15361          */
15362         if(!this.tickable){
15363             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15364             return cfg;
15365         }
15366         
15367         /*
15368          *  ComboBox with tickable selections
15369          */
15370              
15371         var align = this.labelAlign || this.parentLabelAlign();
15372         
15373         cfg = {
15374             cls : 'form-group roo-combobox-tickable' //input-group
15375         };
15376         
15377         var btn_text_select = '';
15378         var btn_text_done = '';
15379         var btn_text_cancel = '';
15380         
15381         if (this.btn_text_show) {
15382             btn_text_select = 'Select';
15383             btn_text_done = 'Done';
15384             btn_text_cancel = 'Cancel'; 
15385         }
15386         
15387         var buttons = {
15388             tag : 'div',
15389             cls : 'tickable-buttons',
15390             cn : [
15391                 {
15392                     tag : 'button',
15393                     type : 'button',
15394                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15395                     //html : this.triggerText
15396                     html: btn_text_select
15397                 },
15398                 {
15399                     tag : 'button',
15400                     type : 'button',
15401                     name : 'ok',
15402                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15403                     //html : 'Done'
15404                     html: btn_text_done
15405                 },
15406                 {
15407                     tag : 'button',
15408                     type : 'button',
15409                     name : 'cancel',
15410                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15411                     //html : 'Cancel'
15412                     html: btn_text_cancel
15413                 }
15414             ]
15415         };
15416         
15417         if(this.editable){
15418             buttons.cn.unshift({
15419                 tag: 'input',
15420                 cls: 'roo-select2-search-field-input'
15421             });
15422         }
15423         
15424         var _this = this;
15425         
15426         Roo.each(buttons.cn, function(c){
15427             if (_this.size) {
15428                 c.cls += ' btn-' + _this.size;
15429             }
15430
15431             if (_this.disabled) {
15432                 c.disabled = true;
15433             }
15434         });
15435         
15436         var box = {
15437             tag: 'div',
15438             style : 'display: contents',
15439             cn: [
15440                 {
15441                     tag: 'input',
15442                     type : 'hidden',
15443                     cls: 'form-hidden-field'
15444                 },
15445                 {
15446                     tag: 'ul',
15447                     cls: 'roo-select2-choices',
15448                     cn:[
15449                         {
15450                             tag: 'li',
15451                             cls: 'roo-select2-search-field',
15452                             cn: [
15453                                 buttons
15454                             ]
15455                         }
15456                     ]
15457                 }
15458             ]
15459         };
15460         
15461         var combobox = {
15462             cls: 'roo-select2-container input-group roo-select2-container-multi',
15463             cn: [
15464                 
15465                 box
15466 //                {
15467 //                    tag: 'ul',
15468 //                    cls: 'typeahead typeahead-long dropdown-menu',
15469 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15470 //                }
15471             ]
15472         };
15473         
15474         if(this.hasFeedback && !this.allowBlank){
15475             
15476             var feedback = {
15477                 tag: 'span',
15478                 cls: 'glyphicon form-control-feedback'
15479             };
15480
15481             combobox.cn.push(feedback);
15482         }
15483         
15484         
15485         
15486         var indicator = {
15487             tag : 'i',
15488             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15489             tooltip : 'This field is required'
15490         };
15491         if (Roo.bootstrap.version == 4) {
15492             indicator = {
15493                 tag : 'i',
15494                 style : 'display:none'
15495             };
15496         }
15497         if (align ==='left' && this.fieldLabel.length) {
15498             
15499             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15500             
15501             cfg.cn = [
15502                 indicator,
15503                 {
15504                     tag: 'label',
15505                     'for' :  id,
15506                     cls : 'control-label col-form-label',
15507                     html : this.fieldLabel
15508
15509                 },
15510                 {
15511                     cls : "", 
15512                     cn: [
15513                         combobox
15514                     ]
15515                 }
15516
15517             ];
15518             
15519             var labelCfg = cfg.cn[1];
15520             var contentCfg = cfg.cn[2];
15521             
15522
15523             if(this.indicatorpos == 'right'){
15524                 
15525                 cfg.cn = [
15526                     {
15527                         tag: 'label',
15528                         'for' :  id,
15529                         cls : 'control-label col-form-label',
15530                         cn : [
15531                             {
15532                                 tag : 'span',
15533                                 html : this.fieldLabel
15534                             },
15535                             indicator
15536                         ]
15537                     },
15538                     {
15539                         cls : "",
15540                         cn: [
15541                             combobox
15542                         ]
15543                     }
15544
15545                 ];
15546                 
15547                 
15548                 
15549                 labelCfg = cfg.cn[0];
15550                 contentCfg = cfg.cn[1];
15551             
15552             }
15553             
15554             if(this.labelWidth > 12){
15555                 labelCfg.style = "width: " + this.labelWidth + 'px';
15556             }
15557             if(this.width * 1 > 0){
15558                 contentCfg.style = "width: " + this.width + 'px';
15559             }
15560             if(this.labelWidth < 13 && this.labelmd == 0){
15561                 this.labelmd = this.labelWidth;
15562             }
15563             
15564             if(this.labellg > 0){
15565                 labelCfg.cls += ' col-lg-' + this.labellg;
15566                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15567             }
15568             
15569             if(this.labelmd > 0){
15570                 labelCfg.cls += ' col-md-' + this.labelmd;
15571                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15572             }
15573             
15574             if(this.labelsm > 0){
15575                 labelCfg.cls += ' col-sm-' + this.labelsm;
15576                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15577             }
15578             
15579             if(this.labelxs > 0){
15580                 labelCfg.cls += ' col-xs-' + this.labelxs;
15581                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15582             }
15583                 
15584                 
15585         } else if ( this.fieldLabel.length) {
15586 //                Roo.log(" label");
15587                  cfg.cn = [
15588                    indicator,
15589                     {
15590                         tag: 'label',
15591                         //cls : 'input-group-addon',
15592                         html : this.fieldLabel
15593                     },
15594                     combobox
15595                 ];
15596                 
15597                 if(this.indicatorpos == 'right'){
15598                     cfg.cn = [
15599                         {
15600                             tag: 'label',
15601                             //cls : 'input-group-addon',
15602                             html : this.fieldLabel
15603                         },
15604                         indicator,
15605                         combobox
15606                     ];
15607                     
15608                 }
15609
15610         } else {
15611             
15612 //                Roo.log(" no label && no align");
15613                 cfg = combobox
15614                      
15615                 
15616         }
15617          
15618         var settings=this;
15619         ['xs','sm','md','lg'].map(function(size){
15620             if (settings[size]) {
15621                 cfg.cls += ' col-' + size + '-' + settings[size];
15622             }
15623         });
15624         
15625         return cfg;
15626         
15627     },
15628     
15629     _initEventsCalled : false,
15630     
15631     // private
15632     initEvents: function()
15633     {   
15634         if (this._initEventsCalled) { // as we call render... prevent looping...
15635             return;
15636         }
15637         this._initEventsCalled = true;
15638         
15639         if (!this.store) {
15640             throw "can not find store for combo";
15641         }
15642         
15643         this.indicator = this.indicatorEl();
15644         
15645         this.store = Roo.factory(this.store, Roo.data);
15646         this.store.parent = this;
15647         
15648         // if we are building from html. then this element is so complex, that we can not really
15649         // use the rendered HTML.
15650         // so we have to trash and replace the previous code.
15651         if (Roo.XComponent.build_from_html) {
15652             // remove this element....
15653             var e = this.el.dom, k=0;
15654             while (e ) { e = e.previousSibling;  ++k;}
15655
15656             this.el.remove();
15657             
15658             this.el=false;
15659             this.rendered = false;
15660             
15661             this.render(this.parent().getChildContainer(true), k);
15662         }
15663         
15664         if(Roo.isIOS && this.useNativeIOS){
15665             this.initIOSView();
15666             return;
15667         }
15668         
15669         /*
15670          * Touch Devices
15671          */
15672         
15673         if(Roo.isTouch && this.mobileTouchView){
15674             this.initTouchView();
15675             return;
15676         }
15677         
15678         if(this.tickable){
15679             this.initTickableEvents();
15680             return;
15681         }
15682         
15683         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15684         
15685         if(this.hiddenName){
15686             
15687             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15688             
15689             this.hiddenField.dom.value =
15690                 this.hiddenValue !== undefined ? this.hiddenValue :
15691                 this.value !== undefined ? this.value : '';
15692
15693             // prevent input submission
15694             this.el.dom.removeAttribute('name');
15695             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15696              
15697              
15698         }
15699         //if(Roo.isGecko){
15700         //    this.el.dom.setAttribute('autocomplete', 'off');
15701         //}
15702         
15703         var cls = 'x-combo-list';
15704         
15705         //this.list = new Roo.Layer({
15706         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15707         //});
15708         
15709         var _this = this;
15710         
15711         (function(){
15712             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15713             _this.list.setWidth(lw);
15714         }).defer(100);
15715         
15716         this.list.on('mouseover', this.onViewOver, this);
15717         this.list.on('mousemove', this.onViewMove, this);
15718         this.list.on('scroll', this.onViewScroll, this);
15719         
15720         /*
15721         this.list.swallowEvent('mousewheel');
15722         this.assetHeight = 0;
15723
15724         if(this.title){
15725             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15726             this.assetHeight += this.header.getHeight();
15727         }
15728
15729         this.innerList = this.list.createChild({cls:cls+'-inner'});
15730         this.innerList.on('mouseover', this.onViewOver, this);
15731         this.innerList.on('mousemove', this.onViewMove, this);
15732         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15733         
15734         if(this.allowBlank && !this.pageSize && !this.disableClear){
15735             this.footer = this.list.createChild({cls:cls+'-ft'});
15736             this.pageTb = new Roo.Toolbar(this.footer);
15737            
15738         }
15739         if(this.pageSize){
15740             this.footer = this.list.createChild({cls:cls+'-ft'});
15741             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15742                     {pageSize: this.pageSize});
15743             
15744         }
15745         
15746         if (this.pageTb && this.allowBlank && !this.disableClear) {
15747             var _this = this;
15748             this.pageTb.add(new Roo.Toolbar.Fill(), {
15749                 cls: 'x-btn-icon x-btn-clear',
15750                 text: '&#160;',
15751                 handler: function()
15752                 {
15753                     _this.collapse();
15754                     _this.clearValue();
15755                     _this.onSelect(false, -1);
15756                 }
15757             });
15758         }
15759         if (this.footer) {
15760             this.assetHeight += this.footer.getHeight();
15761         }
15762         */
15763             
15764         if(!this.tpl){
15765             this.tpl = Roo.bootstrap.version == 4 ?
15766                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15767                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15768         }
15769
15770         this.view = new Roo.View(this.list, this.tpl, {
15771             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15772         });
15773         //this.view.wrapEl.setDisplayed(false);
15774         this.view.on('click', this.onViewClick, this);
15775         
15776         
15777         this.store.on('beforeload', this.onBeforeLoad, this);
15778         this.store.on('load', this.onLoad, this);
15779         this.store.on('loadexception', this.onLoadException, this);
15780         /*
15781         if(this.resizable){
15782             this.resizer = new Roo.Resizable(this.list,  {
15783                pinned:true, handles:'se'
15784             });
15785             this.resizer.on('resize', function(r, w, h){
15786                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15787                 this.listWidth = w;
15788                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15789                 this.restrictHeight();
15790             }, this);
15791             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15792         }
15793         */
15794         if(!this.editable){
15795             this.editable = true;
15796             this.setEditable(false);
15797         }
15798         
15799         /*
15800         
15801         if (typeof(this.events.add.listeners) != 'undefined') {
15802             
15803             this.addicon = this.wrap.createChild(
15804                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15805        
15806             this.addicon.on('click', function(e) {
15807                 this.fireEvent('add', this);
15808             }, this);
15809         }
15810         if (typeof(this.events.edit.listeners) != 'undefined') {
15811             
15812             this.editicon = this.wrap.createChild(
15813                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15814             if (this.addicon) {
15815                 this.editicon.setStyle('margin-left', '40px');
15816             }
15817             this.editicon.on('click', function(e) {
15818                 
15819                 // we fire even  if inothing is selected..
15820                 this.fireEvent('edit', this, this.lastData );
15821                 
15822             }, this);
15823         }
15824         */
15825         
15826         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15827             "up" : function(e){
15828                 this.inKeyMode = true;
15829                 this.selectPrev();
15830             },
15831
15832             "down" : function(e){
15833                 if(!this.isExpanded()){
15834                     this.onTriggerClick();
15835                 }else{
15836                     this.inKeyMode = true;
15837                     this.selectNext();
15838                 }
15839             },
15840
15841             "enter" : function(e){
15842 //                this.onViewClick();
15843                 //return true;
15844                 this.collapse();
15845                 
15846                 if(this.fireEvent("specialkey", this, e)){
15847                     this.onViewClick(false);
15848                 }
15849                 
15850                 return true;
15851             },
15852
15853             "esc" : function(e){
15854                 this.collapse();
15855             },
15856
15857             "tab" : function(e){
15858                 this.collapse();
15859                 
15860                 if(this.fireEvent("specialkey", this, e)){
15861                     this.onViewClick(false);
15862                 }
15863                 
15864                 return true;
15865             },
15866
15867             scope : this,
15868
15869             doRelay : function(foo, bar, hname){
15870                 if(hname == 'down' || this.scope.isExpanded()){
15871                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15872                 }
15873                 return true;
15874             },
15875
15876             forceKeyDown: true
15877         });
15878         
15879         
15880         this.queryDelay = Math.max(this.queryDelay || 10,
15881                 this.mode == 'local' ? 10 : 250);
15882         
15883         
15884         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15885         
15886         if(this.typeAhead){
15887             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15888         }
15889         if(this.editable !== false){
15890             this.inputEl().on("keyup", this.onKeyUp, this);
15891         }
15892         if(this.forceSelection){
15893             this.inputEl().on('blur', this.doForce, this);
15894         }
15895         
15896         if(this.multiple){
15897             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15898             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15899         }
15900     },
15901     
15902     initTickableEvents: function()
15903     {   
15904         this.createList();
15905         
15906         if(this.hiddenName){
15907             
15908             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15909             
15910             this.hiddenField.dom.value =
15911                 this.hiddenValue !== undefined ? this.hiddenValue :
15912                 this.value !== undefined ? this.value : '';
15913
15914             // prevent input submission
15915             this.el.dom.removeAttribute('name');
15916             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15917              
15918              
15919         }
15920         
15921 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15922         
15923         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15924         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15925         if(this.triggerList){
15926             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15927         }
15928          
15929         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15930         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15931         
15932         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15933         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15934         
15935         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15936         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15937         
15938         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15939         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15940         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15941         
15942         this.okBtn.hide();
15943         this.cancelBtn.hide();
15944         
15945         var _this = this;
15946         
15947         (function(){
15948             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15949             _this.list.setWidth(lw);
15950         }).defer(100);
15951         
15952         this.list.on('mouseover', this.onViewOver, this);
15953         this.list.on('mousemove', this.onViewMove, this);
15954         
15955         this.list.on('scroll', this.onViewScroll, this);
15956         
15957         if(!this.tpl){
15958             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15959                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15960         }
15961
15962         this.view = new Roo.View(this.list, this.tpl, {
15963             singleSelect:true,
15964             tickable:true,
15965             parent:this,
15966             store: this.store,
15967             selectedClass: this.selectedClass
15968         });
15969         
15970         //this.view.wrapEl.setDisplayed(false);
15971         this.view.on('click', this.onViewClick, this);
15972         
15973         
15974         
15975         this.store.on('beforeload', this.onBeforeLoad, this);
15976         this.store.on('load', this.onLoad, this);
15977         this.store.on('loadexception', this.onLoadException, this);
15978         
15979         if(this.editable){
15980             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15981                 "up" : function(e){
15982                     this.inKeyMode = true;
15983                     this.selectPrev();
15984                 },
15985
15986                 "down" : function(e){
15987                     this.inKeyMode = true;
15988                     this.selectNext();
15989                 },
15990
15991                 "enter" : function(e){
15992                     if(this.fireEvent("specialkey", this, e)){
15993                         this.onViewClick(false);
15994                     }
15995                     
15996                     return true;
15997                 },
15998
15999                 "esc" : function(e){
16000                     this.onTickableFooterButtonClick(e, false, false);
16001                 },
16002
16003                 "tab" : function(e){
16004                     this.fireEvent("specialkey", this, e);
16005                     
16006                     this.onTickableFooterButtonClick(e, false, false);
16007                     
16008                     return true;
16009                 },
16010
16011                 scope : this,
16012
16013                 doRelay : function(e, fn, key){
16014                     if(this.scope.isExpanded()){
16015                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
16016                     }
16017                     return true;
16018                 },
16019
16020                 forceKeyDown: true
16021             });
16022         }
16023         
16024         this.queryDelay = Math.max(this.queryDelay || 10,
16025                 this.mode == 'local' ? 10 : 250);
16026         
16027         
16028         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
16029         
16030         if(this.typeAhead){
16031             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
16032         }
16033         
16034         if(this.editable !== false){
16035             this.tickableInputEl().on("keyup", this.onKeyUp, this);
16036         }
16037         
16038         this.indicator = this.indicatorEl();
16039         
16040         if(this.indicator){
16041             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
16042             this.indicator.hide();
16043         }
16044         
16045     },
16046
16047     onDestroy : function(){
16048         if(this.view){
16049             this.view.setStore(null);
16050             this.view.el.removeAllListeners();
16051             this.view.el.remove();
16052             this.view.purgeListeners();
16053         }
16054         if(this.list){
16055             this.list.dom.innerHTML  = '';
16056         }
16057         
16058         if(this.store){
16059             this.store.un('beforeload', this.onBeforeLoad, this);
16060             this.store.un('load', this.onLoad, this);
16061             this.store.un('loadexception', this.onLoadException, this);
16062         }
16063         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16064     },
16065
16066     // private
16067     fireKey : function(e){
16068         if(e.isNavKeyPress() && !this.list.isVisible()){
16069             this.fireEvent("specialkey", this, e);
16070         }
16071     },
16072
16073     // private
16074     onResize: function(w, h)
16075     {
16076         
16077         
16078 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16079 //        
16080 //        if(typeof w != 'number'){
16081 //            // we do not handle it!?!?
16082 //            return;
16083 //        }
16084 //        var tw = this.trigger.getWidth();
16085 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16086 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16087 //        var x = w - tw;
16088 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16089 //            
16090 //        //this.trigger.setStyle('left', x+'px');
16091 //        
16092 //        if(this.list && this.listWidth === undefined){
16093 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16094 //            this.list.setWidth(lw);
16095 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16096 //        }
16097         
16098     
16099         
16100     },
16101
16102     /**
16103      * Allow or prevent the user from directly editing the field text.  If false is passed,
16104      * the user will only be able to select from the items defined in the dropdown list.  This method
16105      * is the runtime equivalent of setting the 'editable' config option at config time.
16106      * @param {Boolean} value True to allow the user to directly edit the field text
16107      */
16108     setEditable : function(value){
16109         if(value == this.editable){
16110             return;
16111         }
16112         this.editable = value;
16113         if(!value){
16114             this.inputEl().dom.setAttribute('readOnly', true);
16115             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16116             this.inputEl().addClass('x-combo-noedit');
16117         }else{
16118             this.inputEl().dom.setAttribute('readOnly', false);
16119             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16120             this.inputEl().removeClass('x-combo-noedit');
16121         }
16122     },
16123
16124     // private
16125     
16126     onBeforeLoad : function(combo,opts){
16127         if(!this.hasFocus){
16128             return;
16129         }
16130          if (!opts.add) {
16131             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16132          }
16133         this.restrictHeight();
16134         this.selectedIndex = -1;
16135     },
16136
16137     // private
16138     onLoad : function(){
16139         
16140         this.hasQuery = false;
16141         
16142         if(!this.hasFocus){
16143             return;
16144         }
16145         
16146         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16147             this.loading.hide();
16148         }
16149         
16150         if(this.store.getCount() > 0){
16151             
16152             this.expand();
16153             this.restrictHeight();
16154             if(this.lastQuery == this.allQuery){
16155                 if(this.editable && !this.tickable){
16156                     this.inputEl().dom.select();
16157                 }
16158                 
16159                 if(
16160                     !this.selectByValue(this.value, true) &&
16161                     this.autoFocus && 
16162                     (
16163                         !this.store.lastOptions ||
16164                         typeof(this.store.lastOptions.add) == 'undefined' || 
16165                         this.store.lastOptions.add != true
16166                     )
16167                 ){
16168                     this.select(0, true);
16169                 }
16170             }else{
16171                 if(this.autoFocus){
16172                     this.selectNext();
16173                 }
16174                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16175                     this.taTask.delay(this.typeAheadDelay);
16176                 }
16177             }
16178         }else{
16179             this.onEmptyResults();
16180         }
16181         
16182         //this.el.focus();
16183     },
16184     // private
16185     onLoadException : function()
16186     {
16187         this.hasQuery = false;
16188         
16189         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16190             this.loading.hide();
16191         }
16192         
16193         if(this.tickable && this.editable){
16194             return;
16195         }
16196         
16197         this.collapse();
16198         // only causes errors at present
16199         //Roo.log(this.store.reader.jsonData);
16200         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16201             // fixme
16202             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16203         //}
16204         
16205         
16206     },
16207     // private
16208     onTypeAhead : function(){
16209         if(this.store.getCount() > 0){
16210             var r = this.store.getAt(0);
16211             var newValue = r.data[this.displayField];
16212             var len = newValue.length;
16213             var selStart = this.getRawValue().length;
16214             
16215             if(selStart != len){
16216                 this.setRawValue(newValue);
16217                 this.selectText(selStart, newValue.length);
16218             }
16219         }
16220     },
16221
16222     // private
16223     onSelect : function(record, index){
16224         
16225         if(this.fireEvent('beforeselect', this, record, index) !== false){
16226         
16227             this.setFromData(index > -1 ? record.data : false);
16228             
16229             this.collapse();
16230             this.fireEvent('select', this, record, index);
16231         }
16232     },
16233
16234     /**
16235      * Returns the currently selected field value or empty string if no value is set.
16236      * @return {String} value The selected value
16237      */
16238     getValue : function()
16239     {
16240         if(Roo.isIOS && this.useNativeIOS){
16241             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16242         }
16243         
16244         if(this.multiple){
16245             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16246         }
16247         
16248         if(this.valueField){
16249             return typeof this.value != 'undefined' ? this.value : '';
16250         }else{
16251             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16252         }
16253     },
16254     
16255     getRawValue : function()
16256     {
16257         if(Roo.isIOS && this.useNativeIOS){
16258             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16259         }
16260         
16261         var v = this.inputEl().getValue();
16262         
16263         return v;
16264     },
16265
16266     /**
16267      * Clears any text/value currently set in the field
16268      */
16269     clearValue : function(){
16270         
16271         if(this.hiddenField){
16272             this.hiddenField.dom.value = '';
16273         }
16274         this.value = '';
16275         this.setRawValue('');
16276         this.lastSelectionText = '';
16277         this.lastData = false;
16278         
16279         var close = this.closeTriggerEl();
16280         
16281         if(close){
16282             close.hide();
16283         }
16284         
16285         this.validate();
16286         
16287     },
16288
16289     /**
16290      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16291      * will be displayed in the field.  If the value does not match the data value of an existing item,
16292      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16293      * Otherwise the field will be blank (although the value will still be set).
16294      * @param {String} value The value to match
16295      */
16296     setValue : function(v)
16297     {
16298         if(Roo.isIOS && this.useNativeIOS){
16299             this.setIOSValue(v);
16300             return;
16301         }
16302         
16303         if(this.multiple){
16304             this.syncValue();
16305             return;
16306         }
16307         
16308         var text = v;
16309         if(this.valueField){
16310             var r = this.findRecord(this.valueField, v);
16311             if(r){
16312                 text = r.data[this.displayField];
16313             }else if(this.valueNotFoundText !== undefined){
16314                 text = this.valueNotFoundText;
16315             }
16316         }
16317         this.lastSelectionText = text;
16318         if(this.hiddenField){
16319             this.hiddenField.dom.value = v;
16320         }
16321         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16322         this.value = v;
16323         
16324         var close = this.closeTriggerEl();
16325         
16326         if(close){
16327             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16328         }
16329         
16330         this.validate();
16331     },
16332     /**
16333      * @property {Object} the last set data for the element
16334      */
16335     
16336     lastData : false,
16337     /**
16338      * Sets the value of the field based on a object which is related to the record format for the store.
16339      * @param {Object} value the value to set as. or false on reset?
16340      */
16341     setFromData : function(o){
16342         
16343         if(this.multiple){
16344             this.addItem(o);
16345             return;
16346         }
16347             
16348         var dv = ''; // display value
16349         var vv = ''; // value value..
16350         this.lastData = o;
16351         if (this.displayField) {
16352             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16353         } else {
16354             // this is an error condition!!!
16355             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16356         }
16357         
16358         if(this.valueField){
16359             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16360         }
16361         
16362         var close = this.closeTriggerEl();
16363         
16364         if(close){
16365             if(dv.length || vv * 1 > 0){
16366                 close.show() ;
16367                 this.blockFocus=true;
16368             } else {
16369                 close.hide();
16370             }             
16371         }
16372         
16373         if(this.hiddenField){
16374             this.hiddenField.dom.value = vv;
16375             
16376             this.lastSelectionText = dv;
16377             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16378             this.value = vv;
16379             return;
16380         }
16381         // no hidden field.. - we store the value in 'value', but still display
16382         // display field!!!!
16383         this.lastSelectionText = dv;
16384         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16385         this.value = vv;
16386         
16387         
16388         
16389     },
16390     // private
16391     reset : function(){
16392         // overridden so that last data is reset..
16393         
16394         if(this.multiple){
16395             this.clearItem();
16396             return;
16397         }
16398         
16399         this.setValue(this.originalValue);
16400         //this.clearInvalid();
16401         this.lastData = false;
16402         if (this.view) {
16403             this.view.clearSelections();
16404         }
16405         
16406         this.validate();
16407     },
16408     // private
16409     findRecord : function(prop, value){
16410         var record;
16411         if(this.store.getCount() > 0){
16412             this.store.each(function(r){
16413                 if(r.data[prop] == value){
16414                     record = r;
16415                     return false;
16416                 }
16417                 return true;
16418             });
16419         }
16420         return record;
16421     },
16422     
16423     getName: function()
16424     {
16425         // returns hidden if it's set..
16426         if (!this.rendered) {return ''};
16427         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16428         
16429     },
16430     // private
16431     onViewMove : function(e, t){
16432         this.inKeyMode = false;
16433     },
16434
16435     // private
16436     onViewOver : function(e, t){
16437         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16438             return;
16439         }
16440         var item = this.view.findItemFromChild(t);
16441         
16442         if(item){
16443             var index = this.view.indexOf(item);
16444             this.select(index, false);
16445         }
16446     },
16447
16448     // private
16449     onViewClick : function(view, doFocus, el, e)
16450     {
16451         var index = this.view.getSelectedIndexes()[0];
16452         
16453         var r = this.store.getAt(index);
16454         
16455         if(this.tickable){
16456             
16457             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16458                 return;
16459             }
16460             
16461             var rm = false;
16462             var _this = this;
16463             
16464             Roo.each(this.tickItems, function(v,k){
16465                 
16466                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16467                     Roo.log(v);
16468                     _this.tickItems.splice(k, 1);
16469                     
16470                     if(typeof(e) == 'undefined' && view == false){
16471                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16472                     }
16473                     
16474                     rm = true;
16475                     return;
16476                 }
16477             });
16478             
16479             if(rm){
16480                 return;
16481             }
16482             
16483             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16484                 this.tickItems.push(r.data);
16485             }
16486             
16487             if(typeof(e) == 'undefined' && view == false){
16488                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16489             }
16490                     
16491             return;
16492         }
16493         
16494         if(r){
16495             this.onSelect(r, index);
16496         }
16497         if(doFocus !== false && !this.blockFocus){
16498             this.inputEl().focus();
16499         }
16500     },
16501
16502     // private
16503     restrictHeight : function(){
16504         //this.innerList.dom.style.height = '';
16505         //var inner = this.innerList.dom;
16506         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16507         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16508         //this.list.beginUpdate();
16509         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16510         this.list.alignTo(this.inputEl(), this.listAlign);
16511         this.list.alignTo(this.inputEl(), this.listAlign);
16512         //this.list.endUpdate();
16513     },
16514
16515     // private
16516     onEmptyResults : function(){
16517         
16518         if(this.tickable && this.editable){
16519             this.hasFocus = false;
16520             this.restrictHeight();
16521             return;
16522         }
16523         
16524         this.collapse();
16525     },
16526
16527     /**
16528      * Returns true if the dropdown list is expanded, else false.
16529      */
16530     isExpanded : function(){
16531         return this.list.isVisible();
16532     },
16533
16534     /**
16535      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16536      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16537      * @param {String} value The data value of the item to select
16538      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16539      * selected item if it is not currently in view (defaults to true)
16540      * @return {Boolean} True if the value matched an item in the list, else false
16541      */
16542     selectByValue : function(v, scrollIntoView){
16543         if(v !== undefined && v !== null){
16544             var r = this.findRecord(this.valueField || this.displayField, v);
16545             if(r){
16546                 this.select(this.store.indexOf(r), scrollIntoView);
16547                 return true;
16548             }
16549         }
16550         return false;
16551     },
16552
16553     /**
16554      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16555      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16556      * @param {Number} index The zero-based index of the list item to select
16557      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16558      * selected item if it is not currently in view (defaults to true)
16559      */
16560     select : function(index, scrollIntoView){
16561         this.selectedIndex = index;
16562         this.view.select(index);
16563         if(scrollIntoView !== false){
16564             var el = this.view.getNode(index);
16565             /*
16566              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16567              */
16568             if(el){
16569                 this.list.scrollChildIntoView(el, false);
16570             }
16571         }
16572     },
16573
16574     // private
16575     selectNext : function(){
16576         var ct = this.store.getCount();
16577         if(ct > 0){
16578             if(this.selectedIndex == -1){
16579                 this.select(0);
16580             }else if(this.selectedIndex < ct-1){
16581                 this.select(this.selectedIndex+1);
16582             }
16583         }
16584     },
16585
16586     // private
16587     selectPrev : function(){
16588         var ct = this.store.getCount();
16589         if(ct > 0){
16590             if(this.selectedIndex == -1){
16591                 this.select(0);
16592             }else if(this.selectedIndex != 0){
16593                 this.select(this.selectedIndex-1);
16594             }
16595         }
16596     },
16597
16598     // private
16599     onKeyUp : function(e){
16600         if(this.editable !== false && !e.isSpecialKey()){
16601             this.lastKey = e.getKey();
16602             this.dqTask.delay(this.queryDelay);
16603         }
16604     },
16605
16606     // private
16607     validateBlur : function(){
16608         return !this.list || !this.list.isVisible();   
16609     },
16610
16611     // private
16612     initQuery : function(){
16613         
16614         var v = this.getRawValue();
16615         
16616         if(this.tickable && this.editable){
16617             v = this.tickableInputEl().getValue();
16618         }
16619         
16620         this.doQuery(v);
16621     },
16622
16623     // private
16624     doForce : function(){
16625         if(this.inputEl().dom.value.length > 0){
16626             this.inputEl().dom.value =
16627                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16628              
16629         }
16630     },
16631
16632     /**
16633      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16634      * query allowing the query action to be canceled if needed.
16635      * @param {String} query The SQL query to execute
16636      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16637      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16638      * saved in the current store (defaults to false)
16639      */
16640     doQuery : function(q, forceAll){
16641         
16642         if(q === undefined || q === null){
16643             q = '';
16644         }
16645         var qe = {
16646             query: q,
16647             forceAll: forceAll,
16648             combo: this,
16649             cancel:false
16650         };
16651         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16652             return false;
16653         }
16654         q = qe.query;
16655         
16656         forceAll = qe.forceAll;
16657         if(forceAll === true || (q.length >= this.minChars)){
16658             
16659             this.hasQuery = true;
16660             
16661             if(this.lastQuery != q || this.alwaysQuery){
16662                 this.lastQuery = q;
16663                 if(this.mode == 'local'){
16664                     this.selectedIndex = -1;
16665                     if(forceAll){
16666                         this.store.clearFilter();
16667                     }else{
16668                         
16669                         if(this.specialFilter){
16670                             this.fireEvent('specialfilter', this);
16671                             this.onLoad();
16672                             return;
16673                         }
16674                         
16675                         this.store.filter(this.displayField, q);
16676                     }
16677                     
16678                     this.store.fireEvent("datachanged", this.store);
16679                     
16680                     this.onLoad();
16681                     
16682                     
16683                 }else{
16684                     
16685                     this.store.baseParams[this.queryParam] = q;
16686                     
16687                     var options = {params : this.getParams(q)};
16688                     
16689                     if(this.loadNext){
16690                         options.add = true;
16691                         options.params.start = this.page * this.pageSize;
16692                     }
16693                     
16694                     this.store.load(options);
16695                     
16696                     /*
16697                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16698                      *  we should expand the list on onLoad
16699                      *  so command out it
16700                      */
16701 //                    this.expand();
16702                 }
16703             }else{
16704                 this.selectedIndex = -1;
16705                 this.onLoad();   
16706             }
16707         }
16708         
16709         this.loadNext = false;
16710     },
16711     
16712     // private
16713     getParams : function(q){
16714         var p = {};
16715         //p[this.queryParam] = q;
16716         
16717         if(this.pageSize){
16718             p.start = 0;
16719             p.limit = this.pageSize;
16720         }
16721         return p;
16722     },
16723
16724     /**
16725      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16726      */
16727     collapse : function(){
16728         if(!this.isExpanded()){
16729             return;
16730         }
16731         
16732         this.list.hide();
16733         
16734         this.hasFocus = false;
16735         
16736         if(this.tickable){
16737             this.okBtn.hide();
16738             this.cancelBtn.hide();
16739             this.trigger.show();
16740             
16741             if(this.editable){
16742                 this.tickableInputEl().dom.value = '';
16743                 this.tickableInputEl().blur();
16744             }
16745             
16746         }
16747         
16748         Roo.get(document).un('mousedown', this.collapseIf, this);
16749         Roo.get(document).un('mousewheel', this.collapseIf, this);
16750         if (!this.editable) {
16751             Roo.get(document).un('keydown', this.listKeyPress, this);
16752         }
16753         this.fireEvent('collapse', this);
16754         
16755         this.validate();
16756     },
16757
16758     // private
16759     collapseIf : function(e){
16760         var in_combo  = e.within(this.el);
16761         var in_list =  e.within(this.list);
16762         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16763         
16764         if (in_combo || in_list || is_list) {
16765             //e.stopPropagation();
16766             return;
16767         }
16768         
16769         if(this.tickable){
16770             this.onTickableFooterButtonClick(e, false, false);
16771         }
16772
16773         this.collapse();
16774         
16775     },
16776
16777     /**
16778      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16779      */
16780     expand : function(){
16781        
16782         if(this.isExpanded() || !this.hasFocus){
16783             return;
16784         }
16785         
16786         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16787         this.list.setWidth(lw);
16788         
16789         Roo.log('expand');
16790         
16791         this.list.show();
16792         
16793         this.restrictHeight();
16794         
16795         if(this.tickable){
16796             
16797             this.tickItems = Roo.apply([], this.item);
16798             
16799             this.okBtn.show();
16800             this.cancelBtn.show();
16801             this.trigger.hide();
16802             
16803             if(this.editable){
16804                 this.tickableInputEl().focus();
16805             }
16806             
16807         }
16808         
16809         Roo.get(document).on('mousedown', this.collapseIf, this);
16810         Roo.get(document).on('mousewheel', this.collapseIf, this);
16811         if (!this.editable) {
16812             Roo.get(document).on('keydown', this.listKeyPress, this);
16813         }
16814         
16815         this.fireEvent('expand', this);
16816     },
16817
16818     // private
16819     // Implements the default empty TriggerField.onTriggerClick function
16820     onTriggerClick : function(e)
16821     {
16822         Roo.log('trigger click');
16823         
16824         if(this.disabled || !this.triggerList){
16825             return;
16826         }
16827         
16828         this.page = 0;
16829         this.loadNext = false;
16830         
16831         if(this.isExpanded()){
16832             this.collapse();
16833             if (!this.blockFocus) {
16834                 this.inputEl().focus();
16835             }
16836             
16837         }else {
16838             this.hasFocus = true;
16839             if(this.triggerAction == 'all') {
16840                 this.doQuery(this.allQuery, true);
16841             } else {
16842                 this.doQuery(this.getRawValue());
16843             }
16844             if (!this.blockFocus) {
16845                 this.inputEl().focus();
16846             }
16847         }
16848     },
16849     
16850     onTickableTriggerClick : function(e)
16851     {
16852         if(this.disabled){
16853             return;
16854         }
16855         
16856         this.page = 0;
16857         this.loadNext = false;
16858         this.hasFocus = true;
16859         
16860         if(this.triggerAction == 'all') {
16861             this.doQuery(this.allQuery, true);
16862         } else {
16863             this.doQuery(this.getRawValue());
16864         }
16865     },
16866     
16867     onSearchFieldClick : function(e)
16868     {
16869         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16870             this.onTickableFooterButtonClick(e, false, false);
16871             return;
16872         }
16873         
16874         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16875             return;
16876         }
16877         
16878         this.page = 0;
16879         this.loadNext = false;
16880         this.hasFocus = true;
16881         
16882         if(this.triggerAction == 'all') {
16883             this.doQuery(this.allQuery, true);
16884         } else {
16885             this.doQuery(this.getRawValue());
16886         }
16887     },
16888     
16889     listKeyPress : function(e)
16890     {
16891         //Roo.log('listkeypress');
16892         // scroll to first matching element based on key pres..
16893         if (e.isSpecialKey()) {
16894             return false;
16895         }
16896         var k = String.fromCharCode(e.getKey()).toUpperCase();
16897         //Roo.log(k);
16898         var match  = false;
16899         var csel = this.view.getSelectedNodes();
16900         var cselitem = false;
16901         if (csel.length) {
16902             var ix = this.view.indexOf(csel[0]);
16903             cselitem  = this.store.getAt(ix);
16904             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16905                 cselitem = false;
16906             }
16907             
16908         }
16909         
16910         this.store.each(function(v) { 
16911             if (cselitem) {
16912                 // start at existing selection.
16913                 if (cselitem.id == v.id) {
16914                     cselitem = false;
16915                 }
16916                 return true;
16917             }
16918                 
16919             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16920                 match = this.store.indexOf(v);
16921                 return false;
16922             }
16923             return true;
16924         }, this);
16925         
16926         if (match === false) {
16927             return true; // no more action?
16928         }
16929         // scroll to?
16930         this.view.select(match);
16931         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16932         sn.scrollIntoView(sn.dom.parentNode, false);
16933     },
16934     
16935     onViewScroll : function(e, t){
16936         
16937         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){
16938             return;
16939         }
16940         
16941         this.hasQuery = true;
16942         
16943         this.loading = this.list.select('.loading', true).first();
16944         
16945         if(this.loading === null){
16946             this.list.createChild({
16947                 tag: 'div',
16948                 cls: 'loading roo-select2-more-results roo-select2-active',
16949                 html: 'Loading more results...'
16950             });
16951             
16952             this.loading = this.list.select('.loading', true).first();
16953             
16954             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16955             
16956             this.loading.hide();
16957         }
16958         
16959         this.loading.show();
16960         
16961         var _combo = this;
16962         
16963         this.page++;
16964         this.loadNext = true;
16965         
16966         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16967         
16968         return;
16969     },
16970     
16971     addItem : function(o)
16972     {   
16973         var dv = ''; // display value
16974         
16975         if (this.displayField) {
16976             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16977         } else {
16978             // this is an error condition!!!
16979             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16980         }
16981         
16982         if(!dv.length){
16983             return;
16984         }
16985         
16986         var choice = this.choices.createChild({
16987             tag: 'li',
16988             cls: 'roo-select2-search-choice',
16989             cn: [
16990                 {
16991                     tag: 'div',
16992                     html: dv
16993                 },
16994                 {
16995                     tag: 'a',
16996                     href: '#',
16997                     cls: 'roo-select2-search-choice-close fa fa-times',
16998                     tabindex: '-1'
16999                 }
17000             ]
17001             
17002         }, this.searchField);
17003         
17004         var close = choice.select('a.roo-select2-search-choice-close', true).first();
17005         
17006         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
17007         
17008         this.item.push(o);
17009         
17010         this.lastData = o;
17011         
17012         this.syncValue();
17013         
17014         this.inputEl().dom.value = '';
17015         
17016         this.validate();
17017     },
17018     
17019     onRemoveItem : function(e, _self, o)
17020     {
17021         e.preventDefault();
17022         
17023         this.lastItem = Roo.apply([], this.item);
17024         
17025         var index = this.item.indexOf(o.data) * 1;
17026         
17027         if( index < 0){
17028             Roo.log('not this item?!');
17029             return;
17030         }
17031         
17032         this.item.splice(index, 1);
17033         o.item.remove();
17034         
17035         this.syncValue();
17036         
17037         this.fireEvent('remove', this, e);
17038         
17039         this.validate();
17040         
17041     },
17042     
17043     syncValue : function()
17044     {
17045         if(!this.item.length){
17046             this.clearValue();
17047             return;
17048         }
17049             
17050         var value = [];
17051         var _this = this;
17052         Roo.each(this.item, function(i){
17053             if(_this.valueField){
17054                 value.push(i[_this.valueField]);
17055                 return;
17056             }
17057
17058             value.push(i);
17059         });
17060
17061         this.value = value.join(',');
17062
17063         if(this.hiddenField){
17064             this.hiddenField.dom.value = this.value;
17065         }
17066         
17067         this.store.fireEvent("datachanged", this.store);
17068         
17069         this.validate();
17070     },
17071     
17072     clearItem : function()
17073     {
17074         if(!this.multiple){
17075             return;
17076         }
17077         
17078         this.item = [];
17079         
17080         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17081            c.remove();
17082         });
17083         
17084         this.syncValue();
17085         
17086         this.validate();
17087         
17088         if(this.tickable && !Roo.isTouch){
17089             this.view.refresh();
17090         }
17091     },
17092     
17093     inputEl: function ()
17094     {
17095         if(Roo.isIOS && this.useNativeIOS){
17096             return this.el.select('select.roo-ios-select', true).first();
17097         }
17098         
17099         if(Roo.isTouch && this.mobileTouchView){
17100             return this.el.select('input.form-control',true).first();
17101         }
17102         
17103         if(this.tickable){
17104             return this.searchField;
17105         }
17106         
17107         return this.el.select('input.form-control',true).first();
17108     },
17109     
17110     onTickableFooterButtonClick : function(e, btn, el)
17111     {
17112         e.preventDefault();
17113         
17114         this.lastItem = Roo.apply([], this.item);
17115         
17116         if(btn && btn.name == 'cancel'){
17117             this.tickItems = Roo.apply([], this.item);
17118             this.collapse();
17119             return;
17120         }
17121         
17122         this.clearItem();
17123         
17124         var _this = this;
17125         
17126         Roo.each(this.tickItems, function(o){
17127             _this.addItem(o);
17128         });
17129         
17130         this.collapse();
17131         
17132     },
17133     
17134     validate : function()
17135     {
17136         if(this.getVisibilityEl().hasClass('hidden')){
17137             return true;
17138         }
17139         
17140         var v = this.getRawValue();
17141         
17142         if(this.multiple){
17143             v = this.getValue();
17144         }
17145         
17146         if(this.disabled || this.allowBlank || v.length){
17147             this.markValid();
17148             return true;
17149         }
17150         
17151         this.markInvalid();
17152         return false;
17153     },
17154     
17155     tickableInputEl : function()
17156     {
17157         if(!this.tickable || !this.editable){
17158             return this.inputEl();
17159         }
17160         
17161         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17162     },
17163     
17164     
17165     getAutoCreateTouchView : function()
17166     {
17167         var id = Roo.id();
17168         
17169         var cfg = {
17170             cls: 'form-group' //input-group
17171         };
17172         
17173         var input =  {
17174             tag: 'input',
17175             id : id,
17176             type : this.inputType,
17177             cls : 'form-control x-combo-noedit',
17178             autocomplete: 'new-password',
17179             placeholder : this.placeholder || '',
17180             readonly : true
17181         };
17182         
17183         if (this.name) {
17184             input.name = this.name;
17185         }
17186         
17187         if (this.size) {
17188             input.cls += ' input-' + this.size;
17189         }
17190         
17191         if (this.disabled) {
17192             input.disabled = true;
17193         }
17194         
17195         var inputblock = {
17196             cls : 'roo-combobox-wrap',
17197             cn : [
17198                 input
17199             ]
17200         };
17201         
17202         if(this.before){
17203             inputblock.cls += ' input-group';
17204             
17205             inputblock.cn.unshift({
17206                 tag :'span',
17207                 cls : 'input-group-addon input-group-prepend input-group-text',
17208                 html : this.before
17209             });
17210         }
17211         
17212         if(this.removable && !this.multiple){
17213             inputblock.cls += ' roo-removable';
17214             
17215             inputblock.cn.push({
17216                 tag: 'button',
17217                 html : 'x',
17218                 cls : 'roo-combo-removable-btn close'
17219             });
17220         }
17221
17222         if(this.hasFeedback && !this.allowBlank){
17223             
17224             inputblock.cls += ' has-feedback';
17225             
17226             inputblock.cn.push({
17227                 tag: 'span',
17228                 cls: 'glyphicon form-control-feedback'
17229             });
17230             
17231         }
17232         
17233         if (this.after) {
17234             
17235             inputblock.cls += (this.before) ? '' : ' input-group';
17236             
17237             inputblock.cn.push({
17238                 tag :'span',
17239                 cls : 'input-group-addon input-group-append input-group-text',
17240                 html : this.after
17241             });
17242         }
17243
17244         
17245         var ibwrap = inputblock;
17246         
17247         if(this.multiple){
17248             ibwrap = {
17249                 tag: 'ul',
17250                 cls: 'roo-select2-choices',
17251                 cn:[
17252                     {
17253                         tag: 'li',
17254                         cls: 'roo-select2-search-field',
17255                         cn: [
17256
17257                             inputblock
17258                         ]
17259                     }
17260                 ]
17261             };
17262         
17263             
17264         }
17265         
17266         var combobox = {
17267             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17268             cn: [
17269                 {
17270                     tag: 'input',
17271                     type : 'hidden',
17272                     cls: 'form-hidden-field'
17273                 },
17274                 ibwrap
17275             ]
17276         };
17277         
17278         if(!this.multiple && this.showToggleBtn){
17279             
17280             var caret = {
17281                 cls: 'caret'
17282             };
17283             
17284             if (this.caret != false) {
17285                 caret = {
17286                      tag: 'i',
17287                      cls: 'fa fa-' + this.caret
17288                 };
17289                 
17290             }
17291             
17292             combobox.cn.push({
17293                 tag :'span',
17294                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17295                 cn : [
17296                     Roo.bootstrap.version == 3 ? caret : '',
17297                     {
17298                         tag: 'span',
17299                         cls: 'combobox-clear',
17300                         cn  : [
17301                             {
17302                                 tag : 'i',
17303                                 cls: 'icon-remove'
17304                             }
17305                         ]
17306                     }
17307                 ]
17308
17309             })
17310         }
17311         
17312         if(this.multiple){
17313             combobox.cls += ' roo-select2-container-multi';
17314         }
17315         
17316         var align = this.labelAlign || this.parentLabelAlign();
17317         
17318         if (align ==='left' && this.fieldLabel.length) {
17319
17320             cfg.cn = [
17321                 {
17322                    tag : 'i',
17323                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17324                    tooltip : 'This field is required'
17325                 },
17326                 {
17327                     tag: 'label',
17328                     cls : 'control-label col-form-label',
17329                     html : this.fieldLabel
17330
17331                 },
17332                 {
17333                     cls : 'roo-combobox-wrap ', 
17334                     cn: [
17335                         combobox
17336                     ]
17337                 }
17338             ];
17339             
17340             var labelCfg = cfg.cn[1];
17341             var contentCfg = cfg.cn[2];
17342             
17343
17344             if(this.indicatorpos == 'right'){
17345                 cfg.cn = [
17346                     {
17347                         tag: 'label',
17348                         'for' :  id,
17349                         cls : 'control-label col-form-label',
17350                         cn : [
17351                             {
17352                                 tag : 'span',
17353                                 html : this.fieldLabel
17354                             },
17355                             {
17356                                 tag : 'i',
17357                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17358                                 tooltip : 'This field is required'
17359                             }
17360                         ]
17361                     },
17362                     {
17363                         cls : "roo-combobox-wrap ",
17364                         cn: [
17365                             combobox
17366                         ]
17367                     }
17368
17369                 ];
17370                 
17371                 labelCfg = cfg.cn[0];
17372                 contentCfg = cfg.cn[1];
17373             }
17374             
17375            
17376             
17377             if(this.labelWidth > 12){
17378                 labelCfg.style = "width: " + this.labelWidth + 'px';
17379             }
17380            
17381             if(this.labelWidth < 13 && this.labelmd == 0){
17382                 this.labelmd = this.labelWidth;
17383             }
17384             
17385             if(this.labellg > 0){
17386                 labelCfg.cls += ' col-lg-' + this.labellg;
17387                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17388             }
17389             
17390             if(this.labelmd > 0){
17391                 labelCfg.cls += ' col-md-' + this.labelmd;
17392                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17393             }
17394             
17395             if(this.labelsm > 0){
17396                 labelCfg.cls += ' col-sm-' + this.labelsm;
17397                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17398             }
17399             
17400             if(this.labelxs > 0){
17401                 labelCfg.cls += ' col-xs-' + this.labelxs;
17402                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17403             }
17404                 
17405                 
17406         } else if ( this.fieldLabel.length) {
17407             cfg.cn = [
17408                 {
17409                    tag : 'i',
17410                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17411                    tooltip : 'This field is required'
17412                 },
17413                 {
17414                     tag: 'label',
17415                     cls : 'control-label',
17416                     html : this.fieldLabel
17417
17418                 },
17419                 {
17420                     cls : '', 
17421                     cn: [
17422                         combobox
17423                     ]
17424                 }
17425             ];
17426             
17427             if(this.indicatorpos == 'right'){
17428                 cfg.cn = [
17429                     {
17430                         tag: 'label',
17431                         cls : 'control-label',
17432                         html : this.fieldLabel,
17433                         cn : [
17434                             {
17435                                tag : 'i',
17436                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17437                                tooltip : 'This field is required'
17438                             }
17439                         ]
17440                     },
17441                     {
17442                         cls : '', 
17443                         cn: [
17444                             combobox
17445                         ]
17446                     }
17447                 ];
17448             }
17449         } else {
17450             cfg.cn = combobox;    
17451         }
17452         
17453         
17454         var settings = this;
17455         
17456         ['xs','sm','md','lg'].map(function(size){
17457             if (settings[size]) {
17458                 cfg.cls += ' col-' + size + '-' + settings[size];
17459             }
17460         });
17461         
17462         return cfg;
17463     },
17464     
17465     initTouchView : function()
17466     {
17467         this.renderTouchView();
17468         
17469         this.touchViewEl.on('scroll', function(){
17470             this.el.dom.scrollTop = 0;
17471         }, this);
17472         
17473         this.originalValue = this.getValue();
17474         
17475         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17476         
17477         this.inputEl().on("click", this.showTouchView, this);
17478         if (this.triggerEl) {
17479             this.triggerEl.on("click", this.showTouchView, this);
17480         }
17481         
17482         
17483         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17484         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17485         
17486         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17487         
17488         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17489         this.store.on('load', this.onTouchViewLoad, this);
17490         this.store.on('loadexception', this.onTouchViewLoadException, this);
17491         
17492         if(this.hiddenName){
17493             
17494             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17495             
17496             this.hiddenField.dom.value =
17497                 this.hiddenValue !== undefined ? this.hiddenValue :
17498                 this.value !== undefined ? this.value : '';
17499         
17500             this.el.dom.removeAttribute('name');
17501             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17502         }
17503         
17504         if(this.multiple){
17505             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17506             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17507         }
17508         
17509         if(this.removable && !this.multiple){
17510             var close = this.closeTriggerEl();
17511             if(close){
17512                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17513                 close.on('click', this.removeBtnClick, this, close);
17514             }
17515         }
17516         /*
17517          * fix the bug in Safari iOS8
17518          */
17519         this.inputEl().on("focus", function(e){
17520             document.activeElement.blur();
17521         }, this);
17522         
17523         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17524         
17525         return;
17526         
17527         
17528     },
17529     
17530     renderTouchView : function()
17531     {
17532         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17533         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17534         
17535         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17536         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17537         
17538         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17539         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17540         this.touchViewBodyEl.setStyle('overflow', 'auto');
17541         
17542         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17543         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17544         
17545         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17546         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17547         
17548     },
17549     
17550     showTouchView : function()
17551     {
17552         if(this.disabled){
17553             return;
17554         }
17555         
17556         this.touchViewHeaderEl.hide();
17557
17558         if(this.modalTitle.length){
17559             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17560             this.touchViewHeaderEl.show();
17561         }
17562
17563         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17564         this.touchViewEl.show();
17565
17566         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17567         
17568         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17569         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17570
17571         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17572
17573         if(this.modalTitle.length){
17574             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17575         }
17576         
17577         this.touchViewBodyEl.setHeight(bodyHeight);
17578
17579         if(this.animate){
17580             var _this = this;
17581             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17582         }else{
17583             this.touchViewEl.addClass(['in','show']);
17584         }
17585         
17586         if(this._touchViewMask){
17587             Roo.get(document.body).addClass("x-body-masked");
17588             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17589             this._touchViewMask.setStyle('z-index', 10000);
17590             this._touchViewMask.addClass('show');
17591         }
17592         
17593         this.doTouchViewQuery();
17594         
17595     },
17596     
17597     hideTouchView : function()
17598     {
17599         this.touchViewEl.removeClass(['in','show']);
17600
17601         if(this.animate){
17602             var _this = this;
17603             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17604         }else{
17605             this.touchViewEl.setStyle('display', 'none');
17606         }
17607         
17608         if(this._touchViewMask){
17609             this._touchViewMask.removeClass('show');
17610             Roo.get(document.body).removeClass("x-body-masked");
17611         }
17612     },
17613     
17614     setTouchViewValue : function()
17615     {
17616         if(this.multiple){
17617             this.clearItem();
17618         
17619             var _this = this;
17620
17621             Roo.each(this.tickItems, function(o){
17622                 this.addItem(o);
17623             }, this);
17624         }
17625         
17626         this.hideTouchView();
17627     },
17628     
17629     doTouchViewQuery : function()
17630     {
17631         var qe = {
17632             query: '',
17633             forceAll: true,
17634             combo: this,
17635             cancel:false
17636         };
17637         
17638         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17639             return false;
17640         }
17641         
17642         if(!this.alwaysQuery || this.mode == 'local'){
17643             this.onTouchViewLoad();
17644             return;
17645         }
17646         
17647         this.store.load();
17648     },
17649     
17650     onTouchViewBeforeLoad : function(combo,opts)
17651     {
17652         return;
17653     },
17654
17655     // private
17656     onTouchViewLoad : function()
17657     {
17658         if(this.store.getCount() < 1){
17659             this.onTouchViewEmptyResults();
17660             return;
17661         }
17662         
17663         this.clearTouchView();
17664         
17665         var rawValue = this.getRawValue();
17666         
17667         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17668         
17669         this.tickItems = [];
17670         
17671         this.store.data.each(function(d, rowIndex){
17672             var row = this.touchViewListGroup.createChild(template);
17673             
17674             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17675                 row.addClass(d.data.cls);
17676             }
17677             
17678             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17679                 var cfg = {
17680                     data : d.data,
17681                     html : d.data[this.displayField]
17682                 };
17683                 
17684                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17685                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17686                 }
17687             }
17688             row.removeClass('selected');
17689             if(!this.multiple && this.valueField &&
17690                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17691             {
17692                 // radio buttons..
17693                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17694                 row.addClass('selected');
17695             }
17696             
17697             if(this.multiple && this.valueField &&
17698                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17699             {
17700                 
17701                 // checkboxes...
17702                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17703                 this.tickItems.push(d.data);
17704             }
17705             
17706             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17707             
17708         }, this);
17709         
17710         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17711         
17712         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17713
17714         if(this.modalTitle.length){
17715             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17716         }
17717
17718         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17719         
17720         if(this.mobile_restrict_height && listHeight < bodyHeight){
17721             this.touchViewBodyEl.setHeight(listHeight);
17722         }
17723         
17724         var _this = this;
17725         
17726         if(firstChecked && listHeight > bodyHeight){
17727             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17728         }
17729         
17730     },
17731     
17732     onTouchViewLoadException : function()
17733     {
17734         this.hideTouchView();
17735     },
17736     
17737     onTouchViewEmptyResults : function()
17738     {
17739         this.clearTouchView();
17740         
17741         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17742         
17743         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17744         
17745     },
17746     
17747     clearTouchView : function()
17748     {
17749         this.touchViewListGroup.dom.innerHTML = '';
17750     },
17751     
17752     onTouchViewClick : function(e, el, o)
17753     {
17754         e.preventDefault();
17755         
17756         var row = o.row;
17757         var rowIndex = o.rowIndex;
17758         
17759         var r = this.store.getAt(rowIndex);
17760         
17761         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17762             
17763             if(!this.multiple){
17764                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17765                     c.dom.removeAttribute('checked');
17766                 }, this);
17767
17768                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17769
17770                 this.setFromData(r.data);
17771
17772                 var close = this.closeTriggerEl();
17773
17774                 if(close){
17775                     close.show();
17776                 }
17777
17778                 this.hideTouchView();
17779
17780                 this.fireEvent('select', this, r, rowIndex);
17781
17782                 return;
17783             }
17784
17785             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17786                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17787                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17788                 return;
17789             }
17790
17791             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17792             this.addItem(r.data);
17793             this.tickItems.push(r.data);
17794         }
17795     },
17796     
17797     getAutoCreateNativeIOS : function()
17798     {
17799         var cfg = {
17800             cls: 'form-group' //input-group,
17801         };
17802         
17803         var combobox =  {
17804             tag: 'select',
17805             cls : 'roo-ios-select'
17806         };
17807         
17808         if (this.name) {
17809             combobox.name = this.name;
17810         }
17811         
17812         if (this.disabled) {
17813             combobox.disabled = true;
17814         }
17815         
17816         var settings = this;
17817         
17818         ['xs','sm','md','lg'].map(function(size){
17819             if (settings[size]) {
17820                 cfg.cls += ' col-' + size + '-' + settings[size];
17821             }
17822         });
17823         
17824         cfg.cn = combobox;
17825         
17826         return cfg;
17827         
17828     },
17829     
17830     initIOSView : function()
17831     {
17832         this.store.on('load', this.onIOSViewLoad, this);
17833         
17834         return;
17835     },
17836     
17837     onIOSViewLoad : function()
17838     {
17839         if(this.store.getCount() < 1){
17840             return;
17841         }
17842         
17843         this.clearIOSView();
17844         
17845         if(this.allowBlank) {
17846             
17847             var default_text = '-- SELECT --';
17848             
17849             if(this.placeholder.length){
17850                 default_text = this.placeholder;
17851             }
17852             
17853             if(this.emptyTitle.length){
17854                 default_text += ' - ' + this.emptyTitle + ' -';
17855             }
17856             
17857             var opt = this.inputEl().createChild({
17858                 tag: 'option',
17859                 value : 0,
17860                 html : default_text
17861             });
17862             
17863             var o = {};
17864             o[this.valueField] = 0;
17865             o[this.displayField] = default_text;
17866             
17867             this.ios_options.push({
17868                 data : o,
17869                 el : opt
17870             });
17871             
17872         }
17873         
17874         this.store.data.each(function(d, rowIndex){
17875             
17876             var html = '';
17877             
17878             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17879                 html = d.data[this.displayField];
17880             }
17881             
17882             var value = '';
17883             
17884             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17885                 value = d.data[this.valueField];
17886             }
17887             
17888             var option = {
17889                 tag: 'option',
17890                 value : value,
17891                 html : html
17892             };
17893             
17894             if(this.value == d.data[this.valueField]){
17895                 option['selected'] = true;
17896             }
17897             
17898             var opt = this.inputEl().createChild(option);
17899             
17900             this.ios_options.push({
17901                 data : d.data,
17902                 el : opt
17903             });
17904             
17905         }, this);
17906         
17907         this.inputEl().on('change', function(){
17908            this.fireEvent('select', this);
17909         }, this);
17910         
17911     },
17912     
17913     clearIOSView: function()
17914     {
17915         this.inputEl().dom.innerHTML = '';
17916         
17917         this.ios_options = [];
17918     },
17919     
17920     setIOSValue: function(v)
17921     {
17922         this.value = v;
17923         
17924         if(!this.ios_options){
17925             return;
17926         }
17927         
17928         Roo.each(this.ios_options, function(opts){
17929            
17930            opts.el.dom.removeAttribute('selected');
17931            
17932            if(opts.data[this.valueField] != v){
17933                return;
17934            }
17935            
17936            opts.el.dom.setAttribute('selected', true);
17937            
17938         }, this);
17939     }
17940
17941     /** 
17942     * @cfg {Boolean} grow 
17943     * @hide 
17944     */
17945     /** 
17946     * @cfg {Number} growMin 
17947     * @hide 
17948     */
17949     /** 
17950     * @cfg {Number} growMax 
17951     * @hide 
17952     */
17953     /**
17954      * @hide
17955      * @method autoSize
17956      */
17957 });
17958
17959 Roo.apply(Roo.bootstrap.ComboBox,  {
17960     
17961     header : {
17962         tag: 'div',
17963         cls: 'modal-header',
17964         cn: [
17965             {
17966                 tag: 'h4',
17967                 cls: 'modal-title'
17968             }
17969         ]
17970     },
17971     
17972     body : {
17973         tag: 'div',
17974         cls: 'modal-body',
17975         cn: [
17976             {
17977                 tag: 'ul',
17978                 cls: 'list-group'
17979             }
17980         ]
17981     },
17982     
17983     listItemRadio : {
17984         tag: 'li',
17985         cls: 'list-group-item',
17986         cn: [
17987             {
17988                 tag: 'span',
17989                 cls: 'roo-combobox-list-group-item-value'
17990             },
17991             {
17992                 tag: 'div',
17993                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17994                 cn: [
17995                     {
17996                         tag: 'input',
17997                         type: 'radio'
17998                     },
17999                     {
18000                         tag: 'label'
18001                     }
18002                 ]
18003             }
18004         ]
18005     },
18006     
18007     listItemCheckbox : {
18008         tag: 'li',
18009         cls: 'list-group-item',
18010         cn: [
18011             {
18012                 tag: 'span',
18013                 cls: 'roo-combobox-list-group-item-value'
18014             },
18015             {
18016                 tag: 'div',
18017                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
18018                 cn: [
18019                     {
18020                         tag: 'input',
18021                         type: 'checkbox'
18022                     },
18023                     {
18024                         tag: 'label'
18025                     }
18026                 ]
18027             }
18028         ]
18029     },
18030     
18031     emptyResult : {
18032         tag: 'div',
18033         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
18034     },
18035     
18036     footer : {
18037         tag: 'div',
18038         cls: 'modal-footer',
18039         cn: [
18040             {
18041                 tag: 'div',
18042                 cls: 'row',
18043                 cn: [
18044                     {
18045                         tag: 'div',
18046                         cls: 'col-xs-6 text-left',
18047                         cn: {
18048                             tag: 'button',
18049                             cls: 'btn btn-danger roo-touch-view-cancel',
18050                             html: 'Cancel'
18051                         }
18052                     },
18053                     {
18054                         tag: 'div',
18055                         cls: 'col-xs-6 text-right',
18056                         cn: {
18057                             tag: 'button',
18058                             cls: 'btn btn-success roo-touch-view-ok',
18059                             html: 'OK'
18060                         }
18061                     }
18062                 ]
18063             }
18064         ]
18065         
18066     }
18067 });
18068
18069 Roo.apply(Roo.bootstrap.ComboBox,  {
18070     
18071     touchViewTemplate : {
18072         tag: 'div',
18073         cls: 'modal fade roo-combobox-touch-view',
18074         cn: [
18075             {
18076                 tag: 'div',
18077                 cls: 'modal-dialog',
18078                 style : 'position:fixed', // we have to fix position....
18079                 cn: [
18080                     {
18081                         tag: 'div',
18082                         cls: 'modal-content',
18083                         cn: [
18084                             Roo.bootstrap.ComboBox.header,
18085                             Roo.bootstrap.ComboBox.body,
18086                             Roo.bootstrap.ComboBox.footer
18087                         ]
18088                     }
18089                 ]
18090             }
18091         ]
18092     }
18093 });/*
18094  * Based on:
18095  * Ext JS Library 1.1.1
18096  * Copyright(c) 2006-2007, Ext JS, LLC.
18097  *
18098  * Originally Released Under LGPL - original licence link has changed is not relivant.
18099  *
18100  * Fork - LGPL
18101  * <script type="text/javascript">
18102  */
18103
18104 /**
18105  * @class Roo.View
18106  * @extends Roo.util.Observable
18107  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18108  * This class also supports single and multi selection modes. <br>
18109  * Create a data model bound view:
18110  <pre><code>
18111  var store = new Roo.data.Store(...);
18112
18113  var view = new Roo.View({
18114     el : "my-element",
18115     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18116  
18117     singleSelect: true,
18118     selectedClass: "ydataview-selected",
18119     store: store
18120  });
18121
18122  // listen for node click?
18123  view.on("click", function(vw, index, node, e){
18124  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18125  });
18126
18127  // load XML data
18128  dataModel.load("foobar.xml");
18129  </code></pre>
18130  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18131  * <br><br>
18132  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18133  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18134  * 
18135  * Note: old style constructor is still suported (container, template, config)
18136  * 
18137  * @constructor
18138  * Create a new View
18139  * @param {Object} config The config object
18140  * 
18141  */
18142 Roo.View = function(config, depreciated_tpl, depreciated_config){
18143     
18144     this.parent = false;
18145     
18146     if (typeof(depreciated_tpl) == 'undefined') {
18147         // new way.. - universal constructor.
18148         Roo.apply(this, config);
18149         this.el  = Roo.get(this.el);
18150     } else {
18151         // old format..
18152         this.el  = Roo.get(config);
18153         this.tpl = depreciated_tpl;
18154         Roo.apply(this, depreciated_config);
18155     }
18156     this.wrapEl  = this.el.wrap().wrap();
18157     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18158     
18159     
18160     if(typeof(this.tpl) == "string"){
18161         this.tpl = new Roo.Template(this.tpl);
18162     } else {
18163         // support xtype ctors..
18164         this.tpl = new Roo.factory(this.tpl, Roo);
18165     }
18166     
18167     
18168     this.tpl.compile();
18169     
18170     /** @private */
18171     this.addEvents({
18172         /**
18173          * @event beforeclick
18174          * Fires before a click is processed. Returns false to cancel the default action.
18175          * @param {Roo.View} this
18176          * @param {Number} index The index of the target node
18177          * @param {HTMLElement} node The target node
18178          * @param {Roo.EventObject} e The raw event object
18179          */
18180             "beforeclick" : true,
18181         /**
18182          * @event click
18183          * Fires when a template node is clicked.
18184          * @param {Roo.View} this
18185          * @param {Number} index The index of the target node
18186          * @param {HTMLElement} node The target node
18187          * @param {Roo.EventObject} e The raw event object
18188          */
18189             "click" : true,
18190         /**
18191          * @event dblclick
18192          * Fires when a template node is double clicked.
18193          * @param {Roo.View} this
18194          * @param {Number} index The index of the target node
18195          * @param {HTMLElement} node The target node
18196          * @param {Roo.EventObject} e The raw event object
18197          */
18198             "dblclick" : true,
18199         /**
18200          * @event contextmenu
18201          * Fires when a template node is right clicked.
18202          * @param {Roo.View} this
18203          * @param {Number} index The index of the target node
18204          * @param {HTMLElement} node The target node
18205          * @param {Roo.EventObject} e The raw event object
18206          */
18207             "contextmenu" : true,
18208         /**
18209          * @event selectionchange
18210          * Fires when the selected nodes change.
18211          * @param {Roo.View} this
18212          * @param {Array} selections Array of the selected nodes
18213          */
18214             "selectionchange" : true,
18215     
18216         /**
18217          * @event beforeselect
18218          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18219          * @param {Roo.View} this
18220          * @param {HTMLElement} node The node to be selected
18221          * @param {Array} selections Array of currently selected nodes
18222          */
18223             "beforeselect" : true,
18224         /**
18225          * @event preparedata
18226          * Fires on every row to render, to allow you to change the data.
18227          * @param {Roo.View} this
18228          * @param {Object} data to be rendered (change this)
18229          */
18230           "preparedata" : true
18231           
18232           
18233         });
18234
18235
18236
18237     this.el.on({
18238         "click": this.onClick,
18239         "dblclick": this.onDblClick,
18240         "contextmenu": this.onContextMenu,
18241         scope:this
18242     });
18243
18244     this.selections = [];
18245     this.nodes = [];
18246     this.cmp = new Roo.CompositeElementLite([]);
18247     if(this.store){
18248         this.store = Roo.factory(this.store, Roo.data);
18249         this.setStore(this.store, true);
18250     }
18251     
18252     if ( this.footer && this.footer.xtype) {
18253            
18254          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18255         
18256         this.footer.dataSource = this.store;
18257         this.footer.container = fctr;
18258         this.footer = Roo.factory(this.footer, Roo);
18259         fctr.insertFirst(this.el);
18260         
18261         // this is a bit insane - as the paging toolbar seems to detach the el..
18262 //        dom.parentNode.parentNode.parentNode
18263          // they get detached?
18264     }
18265     
18266     
18267     Roo.View.superclass.constructor.call(this);
18268     
18269     
18270 };
18271
18272 Roo.extend(Roo.View, Roo.util.Observable, {
18273     
18274      /**
18275      * @cfg {Roo.data.Store} store Data store to load data from.
18276      */
18277     store : false,
18278     
18279     /**
18280      * @cfg {String|Roo.Element} el The container element.
18281      */
18282     el : '',
18283     
18284     /**
18285      * @cfg {String|Roo.Template} tpl The template used by this View 
18286      */
18287     tpl : false,
18288     /**
18289      * @cfg {String} dataName the named area of the template to use as the data area
18290      *                          Works with domtemplates roo-name="name"
18291      */
18292     dataName: false,
18293     /**
18294      * @cfg {String} selectedClass The css class to add to selected nodes
18295      */
18296     selectedClass : "x-view-selected",
18297      /**
18298      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18299      */
18300     emptyText : "",
18301     
18302     /**
18303      * @cfg {String} text to display on mask (default Loading)
18304      */
18305     mask : false,
18306     /**
18307      * @cfg {Boolean} multiSelect Allow multiple selection
18308      */
18309     multiSelect : false,
18310     /**
18311      * @cfg {Boolean} singleSelect Allow single selection
18312      */
18313     singleSelect:  false,
18314     
18315     /**
18316      * @cfg {Boolean} toggleSelect - selecting 
18317      */
18318     toggleSelect : false,
18319     
18320     /**
18321      * @cfg {Boolean} tickable - selecting 
18322      */
18323     tickable : false,
18324     
18325     /**
18326      * Returns the element this view is bound to.
18327      * @return {Roo.Element}
18328      */
18329     getEl : function(){
18330         return this.wrapEl;
18331     },
18332     
18333     
18334
18335     /**
18336      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18337      */
18338     refresh : function(){
18339         //Roo.log('refresh');
18340         var t = this.tpl;
18341         
18342         // if we are using something like 'domtemplate', then
18343         // the what gets used is:
18344         // t.applySubtemplate(NAME, data, wrapping data..)
18345         // the outer template then get' applied with
18346         //     the store 'extra data'
18347         // and the body get's added to the
18348         //      roo-name="data" node?
18349         //      <span class='roo-tpl-{name}'></span> ?????
18350         
18351         
18352         
18353         this.clearSelections();
18354         this.el.update("");
18355         var html = [];
18356         var records = this.store.getRange();
18357         if(records.length < 1) {
18358             
18359             // is this valid??  = should it render a template??
18360             
18361             this.el.update(this.emptyText);
18362             return;
18363         }
18364         var el = this.el;
18365         if (this.dataName) {
18366             this.el.update(t.apply(this.store.meta)); //????
18367             el = this.el.child('.roo-tpl-' + this.dataName);
18368         }
18369         
18370         for(var i = 0, len = records.length; i < len; i++){
18371             var data = this.prepareData(records[i].data, i, records[i]);
18372             this.fireEvent("preparedata", this, data, i, records[i]);
18373             
18374             var d = Roo.apply({}, data);
18375             
18376             if(this.tickable){
18377                 Roo.apply(d, {'roo-id' : Roo.id()});
18378                 
18379                 var _this = this;
18380             
18381                 Roo.each(this.parent.item, function(item){
18382                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18383                         return;
18384                     }
18385                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18386                 });
18387             }
18388             
18389             html[html.length] = Roo.util.Format.trim(
18390                 this.dataName ?
18391                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18392                     t.apply(d)
18393             );
18394         }
18395         
18396         
18397         
18398         el.update(html.join(""));
18399         this.nodes = el.dom.childNodes;
18400         this.updateIndexes(0);
18401     },
18402     
18403
18404     /**
18405      * Function to override to reformat the data that is sent to
18406      * the template for each node.
18407      * DEPRICATED - use the preparedata event handler.
18408      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18409      * a JSON object for an UpdateManager bound view).
18410      */
18411     prepareData : function(data, index, record)
18412     {
18413         this.fireEvent("preparedata", this, data, index, record);
18414         return data;
18415     },
18416
18417     onUpdate : function(ds, record){
18418         // Roo.log('on update');   
18419         this.clearSelections();
18420         var index = this.store.indexOf(record);
18421         var n = this.nodes[index];
18422         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18423         n.parentNode.removeChild(n);
18424         this.updateIndexes(index, index);
18425     },
18426
18427     
18428     
18429 // --------- FIXME     
18430     onAdd : function(ds, records, index)
18431     {
18432         //Roo.log(['on Add', ds, records, index] );        
18433         this.clearSelections();
18434         if(this.nodes.length == 0){
18435             this.refresh();
18436             return;
18437         }
18438         var n = this.nodes[index];
18439         for(var i = 0, len = records.length; i < len; i++){
18440             var d = this.prepareData(records[i].data, i, records[i]);
18441             if(n){
18442                 this.tpl.insertBefore(n, d);
18443             }else{
18444                 
18445                 this.tpl.append(this.el, d);
18446             }
18447         }
18448         this.updateIndexes(index);
18449     },
18450
18451     onRemove : function(ds, record, index){
18452        // Roo.log('onRemove');
18453         this.clearSelections();
18454         var el = this.dataName  ?
18455             this.el.child('.roo-tpl-' + this.dataName) :
18456             this.el; 
18457         
18458         el.dom.removeChild(this.nodes[index]);
18459         this.updateIndexes(index);
18460     },
18461
18462     /**
18463      * Refresh an individual node.
18464      * @param {Number} index
18465      */
18466     refreshNode : function(index){
18467         this.onUpdate(this.store, this.store.getAt(index));
18468     },
18469
18470     updateIndexes : function(startIndex, endIndex){
18471         var ns = this.nodes;
18472         startIndex = startIndex || 0;
18473         endIndex = endIndex || ns.length - 1;
18474         for(var i = startIndex; i <= endIndex; i++){
18475             ns[i].nodeIndex = i;
18476         }
18477     },
18478
18479     /**
18480      * Changes the data store this view uses and refresh the view.
18481      * @param {Store} store
18482      */
18483     setStore : function(store, initial){
18484         if(!initial && this.store){
18485             this.store.un("datachanged", this.refresh);
18486             this.store.un("add", this.onAdd);
18487             this.store.un("remove", this.onRemove);
18488             this.store.un("update", this.onUpdate);
18489             this.store.un("clear", this.refresh);
18490             this.store.un("beforeload", this.onBeforeLoad);
18491             this.store.un("load", this.onLoad);
18492             this.store.un("loadexception", this.onLoad);
18493         }
18494         if(store){
18495           
18496             store.on("datachanged", this.refresh, this);
18497             store.on("add", this.onAdd, this);
18498             store.on("remove", this.onRemove, this);
18499             store.on("update", this.onUpdate, this);
18500             store.on("clear", this.refresh, this);
18501             store.on("beforeload", this.onBeforeLoad, this);
18502             store.on("load", this.onLoad, this);
18503             store.on("loadexception", this.onLoad, this);
18504         }
18505         
18506         if(store){
18507             this.refresh();
18508         }
18509     },
18510     /**
18511      * onbeforeLoad - masks the loading area.
18512      *
18513      */
18514     onBeforeLoad : function(store,opts)
18515     {
18516          //Roo.log('onBeforeLoad');   
18517         if (!opts.add) {
18518             this.el.update("");
18519         }
18520         this.el.mask(this.mask ? this.mask : "Loading" ); 
18521     },
18522     onLoad : function ()
18523     {
18524         this.el.unmask();
18525     },
18526     
18527
18528     /**
18529      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18530      * @param {HTMLElement} node
18531      * @return {HTMLElement} The template node
18532      */
18533     findItemFromChild : function(node){
18534         var el = this.dataName  ?
18535             this.el.child('.roo-tpl-' + this.dataName,true) :
18536             this.el.dom; 
18537         
18538         if(!node || node.parentNode == el){
18539                     return node;
18540             }
18541             var p = node.parentNode;
18542             while(p && p != el){
18543             if(p.parentNode == el){
18544                 return p;
18545             }
18546             p = p.parentNode;
18547         }
18548             return null;
18549     },
18550
18551     /** @ignore */
18552     onClick : function(e){
18553         var item = this.findItemFromChild(e.getTarget());
18554         if(item){
18555             var index = this.indexOf(item);
18556             if(this.onItemClick(item, index, e) !== false){
18557                 this.fireEvent("click", this, index, item, e);
18558             }
18559         }else{
18560             this.clearSelections();
18561         }
18562     },
18563
18564     /** @ignore */
18565     onContextMenu : function(e){
18566         var item = this.findItemFromChild(e.getTarget());
18567         if(item){
18568             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18569         }
18570     },
18571
18572     /** @ignore */
18573     onDblClick : function(e){
18574         var item = this.findItemFromChild(e.getTarget());
18575         if(item){
18576             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18577         }
18578     },
18579
18580     onItemClick : function(item, index, e)
18581     {
18582         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18583             return false;
18584         }
18585         if (this.toggleSelect) {
18586             var m = this.isSelected(item) ? 'unselect' : 'select';
18587             //Roo.log(m);
18588             var _t = this;
18589             _t[m](item, true, false);
18590             return true;
18591         }
18592         if(this.multiSelect || this.singleSelect){
18593             if(this.multiSelect && e.shiftKey && this.lastSelection){
18594                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18595             }else{
18596                 this.select(item, this.multiSelect && e.ctrlKey);
18597                 this.lastSelection = item;
18598             }
18599             
18600             if(!this.tickable){
18601                 e.preventDefault();
18602             }
18603             
18604         }
18605         return true;
18606     },
18607
18608     /**
18609      * Get the number of selected nodes.
18610      * @return {Number}
18611      */
18612     getSelectionCount : function(){
18613         return this.selections.length;
18614     },
18615
18616     /**
18617      * Get the currently selected nodes.
18618      * @return {Array} An array of HTMLElements
18619      */
18620     getSelectedNodes : function(){
18621         return this.selections;
18622     },
18623
18624     /**
18625      * Get the indexes of the selected nodes.
18626      * @return {Array}
18627      */
18628     getSelectedIndexes : function(){
18629         var indexes = [], s = this.selections;
18630         for(var i = 0, len = s.length; i < len; i++){
18631             indexes.push(s[i].nodeIndex);
18632         }
18633         return indexes;
18634     },
18635
18636     /**
18637      * Clear all selections
18638      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18639      */
18640     clearSelections : function(suppressEvent){
18641         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18642             this.cmp.elements = this.selections;
18643             this.cmp.removeClass(this.selectedClass);
18644             this.selections = [];
18645             if(!suppressEvent){
18646                 this.fireEvent("selectionchange", this, this.selections);
18647             }
18648         }
18649     },
18650
18651     /**
18652      * Returns true if the passed node is selected
18653      * @param {HTMLElement/Number} node The node or node index
18654      * @return {Boolean}
18655      */
18656     isSelected : function(node){
18657         var s = this.selections;
18658         if(s.length < 1){
18659             return false;
18660         }
18661         node = this.getNode(node);
18662         return s.indexOf(node) !== -1;
18663     },
18664
18665     /**
18666      * Selects nodes.
18667      * @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
18668      * @param {Boolean} keepExisting (optional) true to keep existing selections
18669      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18670      */
18671     select : function(nodeInfo, keepExisting, suppressEvent){
18672         if(nodeInfo instanceof Array){
18673             if(!keepExisting){
18674                 this.clearSelections(true);
18675             }
18676             for(var i = 0, len = nodeInfo.length; i < len; i++){
18677                 this.select(nodeInfo[i], true, true);
18678             }
18679             return;
18680         } 
18681         var node = this.getNode(nodeInfo);
18682         if(!node || this.isSelected(node)){
18683             return; // already selected.
18684         }
18685         if(!keepExisting){
18686             this.clearSelections(true);
18687         }
18688         
18689         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18690             Roo.fly(node).addClass(this.selectedClass);
18691             this.selections.push(node);
18692             if(!suppressEvent){
18693                 this.fireEvent("selectionchange", this, this.selections);
18694             }
18695         }
18696         
18697         
18698     },
18699       /**
18700      * Unselects nodes.
18701      * @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
18702      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18703      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18704      */
18705     unselect : function(nodeInfo, keepExisting, suppressEvent)
18706     {
18707         if(nodeInfo instanceof Array){
18708             Roo.each(this.selections, function(s) {
18709                 this.unselect(s, nodeInfo);
18710             }, this);
18711             return;
18712         }
18713         var node = this.getNode(nodeInfo);
18714         if(!node || !this.isSelected(node)){
18715             //Roo.log("not selected");
18716             return; // not selected.
18717         }
18718         // fireevent???
18719         var ns = [];
18720         Roo.each(this.selections, function(s) {
18721             if (s == node ) {
18722                 Roo.fly(node).removeClass(this.selectedClass);
18723
18724                 return;
18725             }
18726             ns.push(s);
18727         },this);
18728         
18729         this.selections= ns;
18730         this.fireEvent("selectionchange", this, this.selections);
18731     },
18732
18733     /**
18734      * Gets a template node.
18735      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18736      * @return {HTMLElement} The node or null if it wasn't found
18737      */
18738     getNode : function(nodeInfo){
18739         if(typeof nodeInfo == "string"){
18740             return document.getElementById(nodeInfo);
18741         }else if(typeof nodeInfo == "number"){
18742             return this.nodes[nodeInfo];
18743         }
18744         return nodeInfo;
18745     },
18746
18747     /**
18748      * Gets a range template nodes.
18749      * @param {Number} startIndex
18750      * @param {Number} endIndex
18751      * @return {Array} An array of nodes
18752      */
18753     getNodes : function(start, end){
18754         var ns = this.nodes;
18755         start = start || 0;
18756         end = typeof end == "undefined" ? ns.length - 1 : end;
18757         var nodes = [];
18758         if(start <= end){
18759             for(var i = start; i <= end; i++){
18760                 nodes.push(ns[i]);
18761             }
18762         } else{
18763             for(var i = start; i >= end; i--){
18764                 nodes.push(ns[i]);
18765             }
18766         }
18767         return nodes;
18768     },
18769
18770     /**
18771      * Finds the index of the passed node
18772      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18773      * @return {Number} The index of the node or -1
18774      */
18775     indexOf : function(node){
18776         node = this.getNode(node);
18777         if(typeof node.nodeIndex == "number"){
18778             return node.nodeIndex;
18779         }
18780         var ns = this.nodes;
18781         for(var i = 0, len = ns.length; i < len; i++){
18782             if(ns[i] == node){
18783                 return i;
18784             }
18785         }
18786         return -1;
18787     }
18788 });
18789 /*
18790  * - LGPL
18791  *
18792  * based on jquery fullcalendar
18793  * 
18794  */
18795
18796 Roo.bootstrap = Roo.bootstrap || {};
18797 /**
18798  * @class Roo.bootstrap.Calendar
18799  * @extends Roo.bootstrap.Component
18800  * Bootstrap Calendar class
18801  * @cfg {Boolean} loadMask (true|false) default false
18802  * @cfg {Object} header generate the user specific header of the calendar, default false
18803
18804  * @constructor
18805  * Create a new Container
18806  * @param {Object} config The config object
18807  */
18808
18809
18810
18811 Roo.bootstrap.Calendar = function(config){
18812     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18813      this.addEvents({
18814         /**
18815              * @event select
18816              * Fires when a date is selected
18817              * @param {DatePicker} this
18818              * @param {Date} date The selected date
18819              */
18820         'select': true,
18821         /**
18822              * @event monthchange
18823              * Fires when the displayed month changes 
18824              * @param {DatePicker} this
18825              * @param {Date} date The selected month
18826              */
18827         'monthchange': true,
18828         /**
18829              * @event evententer
18830              * Fires when mouse over an event
18831              * @param {Calendar} this
18832              * @param {event} Event
18833              */
18834         'evententer': true,
18835         /**
18836              * @event eventleave
18837              * Fires when the mouse leaves an
18838              * @param {Calendar} this
18839              * @param {event}
18840              */
18841         'eventleave': true,
18842         /**
18843              * @event eventclick
18844              * Fires when the mouse click an
18845              * @param {Calendar} this
18846              * @param {event}
18847              */
18848         'eventclick': true
18849         
18850     });
18851
18852 };
18853
18854 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18855     
18856      /**
18857      * @cfg {Number} startDay
18858      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18859      */
18860     startDay : 0,
18861     
18862     loadMask : false,
18863     
18864     header : false,
18865       
18866     getAutoCreate : function(){
18867         
18868         
18869         var fc_button = function(name, corner, style, content ) {
18870             return Roo.apply({},{
18871                 tag : 'span',
18872                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18873                          (corner.length ?
18874                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18875                             ''
18876                         ),
18877                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18878                 unselectable: 'on'
18879             });
18880         };
18881         
18882         var header = {};
18883         
18884         if(!this.header){
18885             header = {
18886                 tag : 'table',
18887                 cls : 'fc-header',
18888                 style : 'width:100%',
18889                 cn : [
18890                     {
18891                         tag: 'tr',
18892                         cn : [
18893                             {
18894                                 tag : 'td',
18895                                 cls : 'fc-header-left',
18896                                 cn : [
18897                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18898                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18899                                     { tag: 'span', cls: 'fc-header-space' },
18900                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18901
18902
18903                                 ]
18904                             },
18905
18906                             {
18907                                 tag : 'td',
18908                                 cls : 'fc-header-center',
18909                                 cn : [
18910                                     {
18911                                         tag: 'span',
18912                                         cls: 'fc-header-title',
18913                                         cn : {
18914                                             tag: 'H2',
18915                                             html : 'month / year'
18916                                         }
18917                                     }
18918
18919                                 ]
18920                             },
18921                             {
18922                                 tag : 'td',
18923                                 cls : 'fc-header-right',
18924                                 cn : [
18925                               /*      fc_button('month', 'left', '', 'month' ),
18926                                     fc_button('week', '', '', 'week' ),
18927                                     fc_button('day', 'right', '', 'day' )
18928                                 */    
18929
18930                                 ]
18931                             }
18932
18933                         ]
18934                     }
18935                 ]
18936             };
18937         }
18938         
18939         header = this.header;
18940         
18941        
18942         var cal_heads = function() {
18943             var ret = [];
18944             // fixme - handle this.
18945             
18946             for (var i =0; i < Date.dayNames.length; i++) {
18947                 var d = Date.dayNames[i];
18948                 ret.push({
18949                     tag: 'th',
18950                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18951                     html : d.substring(0,3)
18952                 });
18953                 
18954             }
18955             ret[0].cls += ' fc-first';
18956             ret[6].cls += ' fc-last';
18957             return ret;
18958         };
18959         var cal_cell = function(n) {
18960             return  {
18961                 tag: 'td',
18962                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18963                 cn : [
18964                     {
18965                         cn : [
18966                             {
18967                                 cls: 'fc-day-number',
18968                                 html: 'D'
18969                             },
18970                             {
18971                                 cls: 'fc-day-content',
18972                              
18973                                 cn : [
18974                                      {
18975                                         style: 'position: relative;' // height: 17px;
18976                                     }
18977                                 ]
18978                             }
18979                             
18980                             
18981                         ]
18982                     }
18983                 ]
18984                 
18985             }
18986         };
18987         var cal_rows = function() {
18988             
18989             var ret = [];
18990             for (var r = 0; r < 6; r++) {
18991                 var row= {
18992                     tag : 'tr',
18993                     cls : 'fc-week',
18994                     cn : []
18995                 };
18996                 
18997                 for (var i =0; i < Date.dayNames.length; i++) {
18998                     var d = Date.dayNames[i];
18999                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
19000
19001                 }
19002                 row.cn[0].cls+=' fc-first';
19003                 row.cn[0].cn[0].style = 'min-height:90px';
19004                 row.cn[6].cls+=' fc-last';
19005                 ret.push(row);
19006                 
19007             }
19008             ret[0].cls += ' fc-first';
19009             ret[4].cls += ' fc-prev-last';
19010             ret[5].cls += ' fc-last';
19011             return ret;
19012             
19013         };
19014         
19015         var cal_table = {
19016             tag: 'table',
19017             cls: 'fc-border-separate',
19018             style : 'width:100%',
19019             cellspacing  : 0,
19020             cn : [
19021                 { 
19022                     tag: 'thead',
19023                     cn : [
19024                         { 
19025                             tag: 'tr',
19026                             cls : 'fc-first fc-last',
19027                             cn : cal_heads()
19028                         }
19029                     ]
19030                 },
19031                 { 
19032                     tag: 'tbody',
19033                     cn : cal_rows()
19034                 }
19035                   
19036             ]
19037         };
19038          
19039          var cfg = {
19040             cls : 'fc fc-ltr',
19041             cn : [
19042                 header,
19043                 {
19044                     cls : 'fc-content',
19045                     style : "position: relative;",
19046                     cn : [
19047                         {
19048                             cls : 'fc-view fc-view-month fc-grid',
19049                             style : 'position: relative',
19050                             unselectable : 'on',
19051                             cn : [
19052                                 {
19053                                     cls : 'fc-event-container',
19054                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19055                                 },
19056                                 cal_table
19057                             ]
19058                         }
19059                     ]
19060     
19061                 }
19062            ] 
19063             
19064         };
19065         
19066          
19067         
19068         return cfg;
19069     },
19070     
19071     
19072     initEvents : function()
19073     {
19074         if(!this.store){
19075             throw "can not find store for calendar";
19076         }
19077         
19078         var mark = {
19079             tag: "div",
19080             cls:"x-dlg-mask",
19081             style: "text-align:center",
19082             cn: [
19083                 {
19084                     tag: "div",
19085                     style: "background-color:white;width:50%;margin:250 auto",
19086                     cn: [
19087                         {
19088                             tag: "img",
19089                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19090                         },
19091                         {
19092                             tag: "span",
19093                             html: "Loading"
19094                         }
19095                         
19096                     ]
19097                 }
19098             ]
19099         };
19100         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19101         
19102         var size = this.el.select('.fc-content', true).first().getSize();
19103         this.maskEl.setSize(size.width, size.height);
19104         this.maskEl.enableDisplayMode("block");
19105         if(!this.loadMask){
19106             this.maskEl.hide();
19107         }
19108         
19109         this.store = Roo.factory(this.store, Roo.data);
19110         this.store.on('load', this.onLoad, this);
19111         this.store.on('beforeload', this.onBeforeLoad, this);
19112         
19113         this.resize();
19114         
19115         this.cells = this.el.select('.fc-day',true);
19116         //Roo.log(this.cells);
19117         this.textNodes = this.el.query('.fc-day-number');
19118         this.cells.addClassOnOver('fc-state-hover');
19119         
19120         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19121         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19122         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19123         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19124         
19125         this.on('monthchange', this.onMonthChange, this);
19126         
19127         this.update(new Date().clearTime());
19128     },
19129     
19130     resize : function() {
19131         var sz  = this.el.getSize();
19132         
19133         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19134         this.el.select('.fc-day-content div',true).setHeight(34);
19135     },
19136     
19137     
19138     // private
19139     showPrevMonth : function(e){
19140         this.update(this.activeDate.add("mo", -1));
19141     },
19142     showToday : function(e){
19143         this.update(new Date().clearTime());
19144     },
19145     // private
19146     showNextMonth : function(e){
19147         this.update(this.activeDate.add("mo", 1));
19148     },
19149
19150     // private
19151     showPrevYear : function(){
19152         this.update(this.activeDate.add("y", -1));
19153     },
19154
19155     // private
19156     showNextYear : function(){
19157         this.update(this.activeDate.add("y", 1));
19158     },
19159
19160     
19161    // private
19162     update : function(date)
19163     {
19164         var vd = this.activeDate;
19165         this.activeDate = date;
19166 //        if(vd && this.el){
19167 //            var t = date.getTime();
19168 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19169 //                Roo.log('using add remove');
19170 //                
19171 //                this.fireEvent('monthchange', this, date);
19172 //                
19173 //                this.cells.removeClass("fc-state-highlight");
19174 //                this.cells.each(function(c){
19175 //                   if(c.dateValue == t){
19176 //                       c.addClass("fc-state-highlight");
19177 //                       setTimeout(function(){
19178 //                            try{c.dom.firstChild.focus();}catch(e){}
19179 //                       }, 50);
19180 //                       return false;
19181 //                   }
19182 //                   return true;
19183 //                });
19184 //                return;
19185 //            }
19186 //        }
19187         
19188         var days = date.getDaysInMonth();
19189         
19190         var firstOfMonth = date.getFirstDateOfMonth();
19191         var startingPos = firstOfMonth.getDay()-this.startDay;
19192         
19193         if(startingPos < this.startDay){
19194             startingPos += 7;
19195         }
19196         
19197         var pm = date.add(Date.MONTH, -1);
19198         var prevStart = pm.getDaysInMonth()-startingPos;
19199 //        
19200         this.cells = this.el.select('.fc-day',true);
19201         this.textNodes = this.el.query('.fc-day-number');
19202         this.cells.addClassOnOver('fc-state-hover');
19203         
19204         var cells = this.cells.elements;
19205         var textEls = this.textNodes;
19206         
19207         Roo.each(cells, function(cell){
19208             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19209         });
19210         
19211         days += startingPos;
19212
19213         // convert everything to numbers so it's fast
19214         var day = 86400000;
19215         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19216         //Roo.log(d);
19217         //Roo.log(pm);
19218         //Roo.log(prevStart);
19219         
19220         var today = new Date().clearTime().getTime();
19221         var sel = date.clearTime().getTime();
19222         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19223         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19224         var ddMatch = this.disabledDatesRE;
19225         var ddText = this.disabledDatesText;
19226         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19227         var ddaysText = this.disabledDaysText;
19228         var format = this.format;
19229         
19230         var setCellClass = function(cal, cell){
19231             cell.row = 0;
19232             cell.events = [];
19233             cell.more = [];
19234             //Roo.log('set Cell Class');
19235             cell.title = "";
19236             var t = d.getTime();
19237             
19238             //Roo.log(d);
19239             
19240             cell.dateValue = t;
19241             if(t == today){
19242                 cell.className += " fc-today";
19243                 cell.className += " fc-state-highlight";
19244                 cell.title = cal.todayText;
19245             }
19246             if(t == sel){
19247                 // disable highlight in other month..
19248                 //cell.className += " fc-state-highlight";
19249                 
19250             }
19251             // disabling
19252             if(t < min) {
19253                 cell.className = " fc-state-disabled";
19254                 cell.title = cal.minText;
19255                 return;
19256             }
19257             if(t > max) {
19258                 cell.className = " fc-state-disabled";
19259                 cell.title = cal.maxText;
19260                 return;
19261             }
19262             if(ddays){
19263                 if(ddays.indexOf(d.getDay()) != -1){
19264                     cell.title = ddaysText;
19265                     cell.className = " fc-state-disabled";
19266                 }
19267             }
19268             if(ddMatch && format){
19269                 var fvalue = d.dateFormat(format);
19270                 if(ddMatch.test(fvalue)){
19271                     cell.title = ddText.replace("%0", fvalue);
19272                     cell.className = " fc-state-disabled";
19273                 }
19274             }
19275             
19276             if (!cell.initialClassName) {
19277                 cell.initialClassName = cell.dom.className;
19278             }
19279             
19280             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19281         };
19282
19283         var i = 0;
19284         
19285         for(; i < startingPos; i++) {
19286             textEls[i].innerHTML = (++prevStart);
19287             d.setDate(d.getDate()+1);
19288             
19289             cells[i].className = "fc-past fc-other-month";
19290             setCellClass(this, cells[i]);
19291         }
19292         
19293         var intDay = 0;
19294         
19295         for(; i < days; i++){
19296             intDay = i - startingPos + 1;
19297             textEls[i].innerHTML = (intDay);
19298             d.setDate(d.getDate()+1);
19299             
19300             cells[i].className = ''; // "x-date-active";
19301             setCellClass(this, cells[i]);
19302         }
19303         var extraDays = 0;
19304         
19305         for(; i < 42; i++) {
19306             textEls[i].innerHTML = (++extraDays);
19307             d.setDate(d.getDate()+1);
19308             
19309             cells[i].className = "fc-future fc-other-month";
19310             setCellClass(this, cells[i]);
19311         }
19312         
19313         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19314         
19315         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19316         
19317         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19318         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19319         
19320         if(totalRows != 6){
19321             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19322             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19323         }
19324         
19325         this.fireEvent('monthchange', this, date);
19326         
19327         
19328         /*
19329         if(!this.internalRender){
19330             var main = this.el.dom.firstChild;
19331             var w = main.offsetWidth;
19332             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19333             Roo.fly(main).setWidth(w);
19334             this.internalRender = true;
19335             // opera does not respect the auto grow header center column
19336             // then, after it gets a width opera refuses to recalculate
19337             // without a second pass
19338             if(Roo.isOpera && !this.secondPass){
19339                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19340                 this.secondPass = true;
19341                 this.update.defer(10, this, [date]);
19342             }
19343         }
19344         */
19345         
19346     },
19347     
19348     findCell : function(dt) {
19349         dt = dt.clearTime().getTime();
19350         var ret = false;
19351         this.cells.each(function(c){
19352             //Roo.log("check " +c.dateValue + '?=' + dt);
19353             if(c.dateValue == dt){
19354                 ret = c;
19355                 return false;
19356             }
19357             return true;
19358         });
19359         
19360         return ret;
19361     },
19362     
19363     findCells : function(ev) {
19364         var s = ev.start.clone().clearTime().getTime();
19365        // Roo.log(s);
19366         var e= ev.end.clone().clearTime().getTime();
19367        // Roo.log(e);
19368         var ret = [];
19369         this.cells.each(function(c){
19370              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19371             
19372             if(c.dateValue > e){
19373                 return ;
19374             }
19375             if(c.dateValue < s){
19376                 return ;
19377             }
19378             ret.push(c);
19379         });
19380         
19381         return ret;    
19382     },
19383     
19384 //    findBestRow: function(cells)
19385 //    {
19386 //        var ret = 0;
19387 //        
19388 //        for (var i =0 ; i < cells.length;i++) {
19389 //            ret  = Math.max(cells[i].rows || 0,ret);
19390 //        }
19391 //        return ret;
19392 //        
19393 //    },
19394     
19395     
19396     addItem : function(ev)
19397     {
19398         // look for vertical location slot in
19399         var cells = this.findCells(ev);
19400         
19401 //        ev.row = this.findBestRow(cells);
19402         
19403         // work out the location.
19404         
19405         var crow = false;
19406         var rows = [];
19407         for(var i =0; i < cells.length; i++) {
19408             
19409             cells[i].row = cells[0].row;
19410             
19411             if(i == 0){
19412                 cells[i].row = cells[i].row + 1;
19413             }
19414             
19415             if (!crow) {
19416                 crow = {
19417                     start : cells[i],
19418                     end :  cells[i]
19419                 };
19420                 continue;
19421             }
19422             if (crow.start.getY() == cells[i].getY()) {
19423                 // on same row.
19424                 crow.end = cells[i];
19425                 continue;
19426             }
19427             // different row.
19428             rows.push(crow);
19429             crow = {
19430                 start: cells[i],
19431                 end : cells[i]
19432             };
19433             
19434         }
19435         
19436         rows.push(crow);
19437         ev.els = [];
19438         ev.rows = rows;
19439         ev.cells = cells;
19440         
19441         cells[0].events.push(ev);
19442         
19443         this.calevents.push(ev);
19444     },
19445     
19446     clearEvents: function() {
19447         
19448         if(!this.calevents){
19449             return;
19450         }
19451         
19452         Roo.each(this.cells.elements, function(c){
19453             c.row = 0;
19454             c.events = [];
19455             c.more = [];
19456         });
19457         
19458         Roo.each(this.calevents, function(e) {
19459             Roo.each(e.els, function(el) {
19460                 el.un('mouseenter' ,this.onEventEnter, this);
19461                 el.un('mouseleave' ,this.onEventLeave, this);
19462                 el.remove();
19463             },this);
19464         },this);
19465         
19466         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19467             e.remove();
19468         });
19469         
19470     },
19471     
19472     renderEvents: function()
19473     {   
19474         var _this = this;
19475         
19476         this.cells.each(function(c) {
19477             
19478             if(c.row < 5){
19479                 return;
19480             }
19481             
19482             var ev = c.events;
19483             
19484             var r = 4;
19485             if(c.row != c.events.length){
19486                 r = 4 - (4 - (c.row - c.events.length));
19487             }
19488             
19489             c.events = ev.slice(0, r);
19490             c.more = ev.slice(r);
19491             
19492             if(c.more.length && c.more.length == 1){
19493                 c.events.push(c.more.pop());
19494             }
19495             
19496             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19497             
19498         });
19499             
19500         this.cells.each(function(c) {
19501             
19502             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19503             
19504             
19505             for (var e = 0; e < c.events.length; e++){
19506                 var ev = c.events[e];
19507                 var rows = ev.rows;
19508                 
19509                 for(var i = 0; i < rows.length; i++) {
19510                 
19511                     // how many rows should it span..
19512
19513                     var  cfg = {
19514                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19515                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19516
19517                         unselectable : "on",
19518                         cn : [
19519                             {
19520                                 cls: 'fc-event-inner',
19521                                 cn : [
19522     //                                {
19523     //                                  tag:'span',
19524     //                                  cls: 'fc-event-time',
19525     //                                  html : cells.length > 1 ? '' : ev.time
19526     //                                },
19527                                     {
19528                                       tag:'span',
19529                                       cls: 'fc-event-title',
19530                                       html : String.format('{0}', ev.title)
19531                                     }
19532
19533
19534                                 ]
19535                             },
19536                             {
19537                                 cls: 'ui-resizable-handle ui-resizable-e',
19538                                 html : '&nbsp;&nbsp;&nbsp'
19539                             }
19540
19541                         ]
19542                     };
19543
19544                     if (i == 0) {
19545                         cfg.cls += ' fc-event-start';
19546                     }
19547                     if ((i+1) == rows.length) {
19548                         cfg.cls += ' fc-event-end';
19549                     }
19550
19551                     var ctr = _this.el.select('.fc-event-container',true).first();
19552                     var cg = ctr.createChild(cfg);
19553
19554                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19555                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19556
19557                     var r = (c.more.length) ? 1 : 0;
19558                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19559                     cg.setWidth(ebox.right - sbox.x -2);
19560
19561                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19562                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19563                     cg.on('click', _this.onEventClick, _this, ev);
19564
19565                     ev.els.push(cg);
19566                     
19567                 }
19568                 
19569             }
19570             
19571             
19572             if(c.more.length){
19573                 var  cfg = {
19574                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19575                     style : 'position: absolute',
19576                     unselectable : "on",
19577                     cn : [
19578                         {
19579                             cls: 'fc-event-inner',
19580                             cn : [
19581                                 {
19582                                   tag:'span',
19583                                   cls: 'fc-event-title',
19584                                   html : 'More'
19585                                 }
19586
19587
19588                             ]
19589                         },
19590                         {
19591                             cls: 'ui-resizable-handle ui-resizable-e',
19592                             html : '&nbsp;&nbsp;&nbsp'
19593                         }
19594
19595                     ]
19596                 };
19597
19598                 var ctr = _this.el.select('.fc-event-container',true).first();
19599                 var cg = ctr.createChild(cfg);
19600
19601                 var sbox = c.select('.fc-day-content',true).first().getBox();
19602                 var ebox = c.select('.fc-day-content',true).first().getBox();
19603                 //Roo.log(cg);
19604                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19605                 cg.setWidth(ebox.right - sbox.x -2);
19606
19607                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19608                 
19609             }
19610             
19611         });
19612         
19613         
19614         
19615     },
19616     
19617     onEventEnter: function (e, el,event,d) {
19618         this.fireEvent('evententer', this, el, event);
19619     },
19620     
19621     onEventLeave: function (e, el,event,d) {
19622         this.fireEvent('eventleave', this, el, event);
19623     },
19624     
19625     onEventClick: function (e, el,event,d) {
19626         this.fireEvent('eventclick', this, el, event);
19627     },
19628     
19629     onMonthChange: function () {
19630         this.store.load();
19631     },
19632     
19633     onMoreEventClick: function(e, el, more)
19634     {
19635         var _this = this;
19636         
19637         this.calpopover.placement = 'right';
19638         this.calpopover.setTitle('More');
19639         
19640         this.calpopover.setContent('');
19641         
19642         var ctr = this.calpopover.el.select('.popover-content', true).first();
19643         
19644         Roo.each(more, function(m){
19645             var cfg = {
19646                 cls : 'fc-event-hori fc-event-draggable',
19647                 html : m.title
19648             };
19649             var cg = ctr.createChild(cfg);
19650             
19651             cg.on('click', _this.onEventClick, _this, m);
19652         });
19653         
19654         this.calpopover.show(el);
19655         
19656         
19657     },
19658     
19659     onLoad: function () 
19660     {   
19661         this.calevents = [];
19662         var cal = this;
19663         
19664         if(this.store.getCount() > 0){
19665             this.store.data.each(function(d){
19666                cal.addItem({
19667                     id : d.data.id,
19668                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19669                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19670                     time : d.data.start_time,
19671                     title : d.data.title,
19672                     description : d.data.description,
19673                     venue : d.data.venue
19674                 });
19675             });
19676         }
19677         
19678         this.renderEvents();
19679         
19680         if(this.calevents.length && this.loadMask){
19681             this.maskEl.hide();
19682         }
19683     },
19684     
19685     onBeforeLoad: function()
19686     {
19687         this.clearEvents();
19688         if(this.loadMask){
19689             this.maskEl.show();
19690         }
19691     }
19692 });
19693
19694  
19695  /*
19696  * - LGPL
19697  *
19698  * element
19699  * 
19700  */
19701
19702 /**
19703  * @class Roo.bootstrap.Popover
19704  * @extends Roo.bootstrap.Component
19705  * Bootstrap Popover class
19706  * @cfg {String} html contents of the popover   (or false to use children..)
19707  * @cfg {String} title of popover (or false to hide)
19708  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19709  * @cfg {String} trigger click || hover (or false to trigger manually)
19710  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19711  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19712  *      - if false and it has a 'parent' then it will be automatically added to that element
19713  *      - if string - Roo.get  will be called 
19714  * @cfg {Number} delay - delay before showing
19715  
19716  * @constructor
19717  * Create a new Popover
19718  * @param {Object} config The config object
19719  */
19720
19721 Roo.bootstrap.Popover = function(config){
19722     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19723     
19724     this.addEvents({
19725         // raw events
19726          /**
19727          * @event show
19728          * After the popover show
19729          * 
19730          * @param {Roo.bootstrap.Popover} this
19731          */
19732         "show" : true,
19733         /**
19734          * @event hide
19735          * After the popover hide
19736          * 
19737          * @param {Roo.bootstrap.Popover} this
19738          */
19739         "hide" : true
19740     });
19741 };
19742
19743 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19744     
19745     title: false,
19746     html: false,
19747     
19748     placement : 'right',
19749     trigger : 'hover', // hover
19750     modal : false,
19751     delay : 0,
19752     
19753     over: false,
19754     
19755     can_build_overlaid : false,
19756     
19757     maskEl : false, // the mask element
19758     headerEl : false,
19759     contentEl : false,
19760     alignEl : false, // when show is called with an element - this get's stored.
19761     
19762     getChildContainer : function()
19763     {
19764         return this.contentEl;
19765         
19766     },
19767     getPopoverHeader : function()
19768     {
19769         this.title = true; // flag not to hide it..
19770         this.headerEl.addClass('p-0');
19771         return this.headerEl
19772     },
19773     
19774     
19775     getAutoCreate : function(){
19776          
19777         var cfg = {
19778            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19779            style: 'display:block',
19780            cn : [
19781                 {
19782                     cls : 'arrow'
19783                 },
19784                 {
19785                     cls : 'popover-inner ',
19786                     cn : [
19787                         {
19788                             tag: 'h3',
19789                             cls: 'popover-title popover-header',
19790                             html : this.title === false ? '' : this.title
19791                         },
19792                         {
19793                             cls : 'popover-content popover-body '  + (this.cls || ''),
19794                             html : this.html || ''
19795                         }
19796                     ]
19797                     
19798                 }
19799            ]
19800         };
19801         
19802         return cfg;
19803     },
19804     /**
19805      * @param {string} the title
19806      */
19807     setTitle: function(str)
19808     {
19809         this.title = str;
19810         if (this.el) {
19811             this.headerEl.dom.innerHTML = str;
19812         }
19813         
19814     },
19815     /**
19816      * @param {string} the body content
19817      */
19818     setContent: function(str)
19819     {
19820         this.html = str;
19821         if (this.contentEl) {
19822             this.contentEl.dom.innerHTML = str;
19823         }
19824         
19825     },
19826     // as it get's added to the bottom of the page.
19827     onRender : function(ct, position)
19828     {
19829         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19830         
19831         
19832         
19833         if(!this.el){
19834             var cfg = Roo.apply({},  this.getAutoCreate());
19835             cfg.id = Roo.id();
19836             
19837             if (this.cls) {
19838                 cfg.cls += ' ' + this.cls;
19839             }
19840             if (this.style) {
19841                 cfg.style = this.style;
19842             }
19843             //Roo.log("adding to ");
19844             this.el = Roo.get(document.body).createChild(cfg, position);
19845 //            Roo.log(this.el);
19846         }
19847         
19848         this.contentEl = this.el.select('.popover-content',true).first();
19849         this.headerEl =  this.el.select('.popover-title',true).first();
19850         
19851         var nitems = [];
19852         if(typeof(this.items) != 'undefined'){
19853             var items = this.items;
19854             delete this.items;
19855
19856             for(var i =0;i < items.length;i++) {
19857                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19858             }
19859         }
19860
19861         this.items = nitems;
19862         
19863         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19864         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19865         
19866         
19867         
19868         this.initEvents();
19869     },
19870     
19871     resizeMask : function()
19872     {
19873         this.maskEl.setSize(
19874             Roo.lib.Dom.getViewWidth(true),
19875             Roo.lib.Dom.getViewHeight(true)
19876         );
19877     },
19878     
19879     initEvents : function()
19880     {
19881         
19882         if (!this.modal) { 
19883             Roo.bootstrap.Popover.register(this);
19884         }
19885          
19886         this.arrowEl = this.el.select('.arrow',true).first();
19887         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19888         this.el.enableDisplayMode('block');
19889         this.el.hide();
19890  
19891         
19892         if (this.over === false && !this.parent()) {
19893             return; 
19894         }
19895         if (this.triggers === false) {
19896             return;
19897         }
19898          
19899         // support parent
19900         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19901         var triggers = this.trigger ? this.trigger.split(' ') : [];
19902         Roo.each(triggers, function(trigger) {
19903         
19904             if (trigger == 'click') {
19905                 on_el.on('click', this.toggle, this);
19906             } else if (trigger != 'manual') {
19907                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19908                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19909       
19910                 on_el.on(eventIn  ,this.enter, this);
19911                 on_el.on(eventOut, this.leave, this);
19912             }
19913         }, this);
19914     },
19915     
19916     
19917     // private
19918     timeout : null,
19919     hoverState : null,
19920     
19921     toggle : function () {
19922         this.hoverState == 'in' ? this.leave() : this.enter();
19923     },
19924     
19925     enter : function () {
19926         
19927         clearTimeout(this.timeout);
19928     
19929         this.hoverState = 'in';
19930     
19931         if (!this.delay || !this.delay.show) {
19932             this.show();
19933             return;
19934         }
19935         var _t = this;
19936         this.timeout = setTimeout(function () {
19937             if (_t.hoverState == 'in') {
19938                 _t.show();
19939             }
19940         }, this.delay.show)
19941     },
19942     
19943     leave : function() {
19944         clearTimeout(this.timeout);
19945     
19946         this.hoverState = 'out';
19947     
19948         if (!this.delay || !this.delay.hide) {
19949             this.hide();
19950             return;
19951         }
19952         var _t = this;
19953         this.timeout = setTimeout(function () {
19954             if (_t.hoverState == 'out') {
19955                 _t.hide();
19956             }
19957         }, this.delay.hide)
19958     },
19959     /**
19960      * Show the popover
19961      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19962      * @param {string} (left|right|top|bottom) position
19963      */
19964     show : function (on_el, placement)
19965     {
19966         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19967         on_el = on_el || false; // default to false
19968          
19969         if (!on_el) {
19970             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19971                 on_el = this.parent().el;
19972             } else if (this.over) {
19973                 Roo.get(this.over);
19974             }
19975             
19976         }
19977         
19978         this.alignEl = Roo.get( on_el );
19979
19980         if (!this.el) {
19981             this.render(document.body);
19982         }
19983         
19984         
19985          
19986         
19987         if (this.title === false) {
19988             this.headerEl.hide();
19989         }
19990         
19991        
19992         this.el.show();
19993         this.el.dom.style.display = 'block';
19994          
19995  
19996         if (this.alignEl) {
19997             this.updatePosition(this.placement, true);
19998              
19999         } else {
20000             // this is usually just done by the builder = to show the popoup in the middle of the scren.
20001             var es = this.el.getSize();
20002             var x = Roo.lib.Dom.getViewWidth()/2;
20003             var y = Roo.lib.Dom.getViewHeight()/2;
20004             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
20005             
20006         }
20007
20008         
20009         //var arrow = this.el.select('.arrow',true).first();
20010         //arrow.set(align[2], 
20011         
20012         this.el.addClass('in');
20013         
20014          
20015         
20016         this.hoverState = 'in';
20017         
20018         if (this.modal) {
20019             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
20020             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20021             this.maskEl.dom.style.display = 'block';
20022             this.maskEl.addClass('show');
20023         }
20024         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
20025  
20026         this.fireEvent('show', this);
20027         
20028     },
20029     /**
20030      * fire this manually after loading a grid in the table for example
20031      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
20032      * @param {Boolean} try and move it if we cant get right position.
20033      */
20034     updatePosition : function(placement, try_move)
20035     {
20036         // allow for calling with no parameters
20037         placement = placement   ? placement :  this.placement;
20038         try_move = typeof(try_move) == 'undefined' ? true : try_move;
20039         
20040         this.el.removeClass([
20041             'fade','top','bottom', 'left', 'right','in',
20042             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
20043         ]);
20044         this.el.addClass(placement + ' bs-popover-' + placement);
20045         
20046         if (!this.alignEl ) {
20047             return false;
20048         }
20049         
20050         switch (placement) {
20051             case 'right':
20052                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20053                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20054                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20055                     //normal display... or moved up/down.
20056                     this.el.setXY(offset);
20057                     var xy = this.alignEl.getAnchorXY('tr', false);
20058                     xy[0]+=2;xy[1]+=5;
20059                     this.arrowEl.setXY(xy);
20060                     return true;
20061                 }
20062                 // continue through...
20063                 return this.updatePosition('left', false);
20064                 
20065             
20066             case 'left':
20067                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20068                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20069                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20070                     //normal display... or moved up/down.
20071                     this.el.setXY(offset);
20072                     var xy = this.alignEl.getAnchorXY('tl', false);
20073                     xy[0]-=10;xy[1]+=5; // << fix me
20074                     this.arrowEl.setXY(xy);
20075                     return true;
20076                 }
20077                 // call self...
20078                 return this.updatePosition('right', false);
20079             
20080             case 'top':
20081                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20082                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20083                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20084                     //normal display... or moved up/down.
20085                     this.el.setXY(offset);
20086                     var xy = this.alignEl.getAnchorXY('t', false);
20087                     xy[1]-=10; // << fix me
20088                     this.arrowEl.setXY(xy);
20089                     return true;
20090                 }
20091                 // fall through
20092                return this.updatePosition('bottom', false);
20093             
20094             case 'bottom':
20095                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20096                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20097                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20098                     //normal display... or moved up/down.
20099                     this.el.setXY(offset);
20100                     var xy = this.alignEl.getAnchorXY('b', false);
20101                      xy[1]+=2; // << fix me
20102                     this.arrowEl.setXY(xy);
20103                     return true;
20104                 }
20105                 // fall through
20106                 return this.updatePosition('top', false);
20107                 
20108             
20109         }
20110         
20111         
20112         return false;
20113     },
20114     
20115     hide : function()
20116     {
20117         this.el.setXY([0,0]);
20118         this.el.removeClass('in');
20119         this.el.hide();
20120         this.hoverState = null;
20121         this.maskEl.hide(); // always..
20122         this.fireEvent('hide', this);
20123     }
20124     
20125 });
20126
20127
20128 Roo.apply(Roo.bootstrap.Popover, {
20129
20130     alignment : {
20131         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20132         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20133         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20134         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20135     },
20136     
20137     zIndex : 20001,
20138
20139     clickHander : false,
20140     
20141
20142     onMouseDown : function(e)
20143     {
20144         if (!e.getTarget(".roo-popover")) {
20145             this.hideAll();
20146         }
20147          
20148     },
20149     
20150     popups : [],
20151     
20152     register : function(popup)
20153     {
20154         if (!Roo.bootstrap.Popover.clickHandler) {
20155             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20156         }
20157         // hide other popups.
20158         this.hideAll();
20159         this.popups.push(popup);
20160     },
20161     hideAll : function()
20162     {
20163         this.popups.forEach(function(p) {
20164             p.hide();
20165         });
20166     }
20167
20168 });/*
20169  * - LGPL
20170  *
20171  * Card header - holder for the card header elements.
20172  * 
20173  */
20174
20175 /**
20176  * @class Roo.bootstrap.PopoverNav
20177  * @extends Roo.bootstrap.NavGroup
20178  * Bootstrap Popover header navigation class
20179  * @constructor
20180  * Create a new Popover Header Navigation 
20181  * @param {Object} config The config object
20182  */
20183
20184 Roo.bootstrap.PopoverNav = function(config){
20185     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20186 };
20187
20188 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20189     
20190     
20191     container_method : 'getPopoverHeader' 
20192     
20193      
20194     
20195     
20196    
20197 });
20198
20199  
20200
20201  /*
20202  * - LGPL
20203  *
20204  * Progress
20205  * 
20206  */
20207
20208 /**
20209  * @class Roo.bootstrap.Progress
20210  * @extends Roo.bootstrap.Component
20211  * Bootstrap Progress class
20212  * @cfg {Boolean} striped striped of the progress bar
20213  * @cfg {Boolean} active animated of the progress bar
20214  * 
20215  * 
20216  * @constructor
20217  * Create a new Progress
20218  * @param {Object} config The config object
20219  */
20220
20221 Roo.bootstrap.Progress = function(config){
20222     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20223 };
20224
20225 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20226     
20227     striped : false,
20228     active: false,
20229     
20230     getAutoCreate : function(){
20231         var cfg = {
20232             tag: 'div',
20233             cls: 'progress'
20234         };
20235         
20236         
20237         if(this.striped){
20238             cfg.cls += ' progress-striped';
20239         }
20240       
20241         if(this.active){
20242             cfg.cls += ' active';
20243         }
20244         
20245         
20246         return cfg;
20247     }
20248    
20249 });
20250
20251  
20252
20253  /*
20254  * - LGPL
20255  *
20256  * ProgressBar
20257  * 
20258  */
20259
20260 /**
20261  * @class Roo.bootstrap.ProgressBar
20262  * @extends Roo.bootstrap.Component
20263  * Bootstrap ProgressBar class
20264  * @cfg {Number} aria_valuenow aria-value now
20265  * @cfg {Number} aria_valuemin aria-value min
20266  * @cfg {Number} aria_valuemax aria-value max
20267  * @cfg {String} label label for the progress bar
20268  * @cfg {String} panel (success | info | warning | danger )
20269  * @cfg {String} role role of the progress bar
20270  * @cfg {String} sr_only text
20271  * 
20272  * 
20273  * @constructor
20274  * Create a new ProgressBar
20275  * @param {Object} config The config object
20276  */
20277
20278 Roo.bootstrap.ProgressBar = function(config){
20279     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20280 };
20281
20282 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20283     
20284     aria_valuenow : 0,
20285     aria_valuemin : 0,
20286     aria_valuemax : 100,
20287     label : false,
20288     panel : false,
20289     role : false,
20290     sr_only: false,
20291     
20292     getAutoCreate : function()
20293     {
20294         
20295         var cfg = {
20296             tag: 'div',
20297             cls: 'progress-bar',
20298             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20299         };
20300         
20301         if(this.sr_only){
20302             cfg.cn = {
20303                 tag: 'span',
20304                 cls: 'sr-only',
20305                 html: this.sr_only
20306             }
20307         }
20308         
20309         if(this.role){
20310             cfg.role = this.role;
20311         }
20312         
20313         if(this.aria_valuenow){
20314             cfg['aria-valuenow'] = this.aria_valuenow;
20315         }
20316         
20317         if(this.aria_valuemin){
20318             cfg['aria-valuemin'] = this.aria_valuemin;
20319         }
20320         
20321         if(this.aria_valuemax){
20322             cfg['aria-valuemax'] = this.aria_valuemax;
20323         }
20324         
20325         if(this.label && !this.sr_only){
20326             cfg.html = this.label;
20327         }
20328         
20329         if(this.panel){
20330             cfg.cls += ' progress-bar-' + this.panel;
20331         }
20332         
20333         return cfg;
20334     },
20335     
20336     update : function(aria_valuenow)
20337     {
20338         this.aria_valuenow = aria_valuenow;
20339         
20340         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20341     }
20342    
20343 });
20344
20345  
20346
20347  /*
20348  * - LGPL
20349  *
20350  * column
20351  * 
20352  */
20353
20354 /**
20355  * @class Roo.bootstrap.TabGroup
20356  * @extends Roo.bootstrap.Column
20357  * Bootstrap Column class
20358  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20359  * @cfg {Boolean} carousel true to make the group behave like a carousel
20360  * @cfg {Boolean} bullets show bullets for the panels
20361  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20362  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20363  * @cfg {Boolean} showarrow (true|false) show arrow default true
20364  * 
20365  * @constructor
20366  * Create a new TabGroup
20367  * @param {Object} config The config object
20368  */
20369
20370 Roo.bootstrap.TabGroup = function(config){
20371     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20372     if (!this.navId) {
20373         this.navId = Roo.id();
20374     }
20375     this.tabs = [];
20376     Roo.bootstrap.TabGroup.register(this);
20377     
20378 };
20379
20380 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20381     
20382     carousel : false,
20383     transition : false,
20384     bullets : 0,
20385     timer : 0,
20386     autoslide : false,
20387     slideFn : false,
20388     slideOnTouch : false,
20389     showarrow : true,
20390     
20391     getAutoCreate : function()
20392     {
20393         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20394         
20395         cfg.cls += ' tab-content';
20396         
20397         if (this.carousel) {
20398             cfg.cls += ' carousel slide';
20399             
20400             cfg.cn = [{
20401                cls : 'carousel-inner',
20402                cn : []
20403             }];
20404         
20405             if(this.bullets  && !Roo.isTouch){
20406                 
20407                 var bullets = {
20408                     cls : 'carousel-bullets',
20409                     cn : []
20410                 };
20411                
20412                 if(this.bullets_cls){
20413                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20414                 }
20415                 
20416                 bullets.cn.push({
20417                     cls : 'clear'
20418                 });
20419                 
20420                 cfg.cn[0].cn.push(bullets);
20421             }
20422             
20423             if(this.showarrow){
20424                 cfg.cn[0].cn.push({
20425                     tag : 'div',
20426                     class : 'carousel-arrow',
20427                     cn : [
20428                         {
20429                             tag : 'div',
20430                             class : 'carousel-prev',
20431                             cn : [
20432                                 {
20433                                     tag : 'i',
20434                                     class : 'fa fa-chevron-left'
20435                                 }
20436                             ]
20437                         },
20438                         {
20439                             tag : 'div',
20440                             class : 'carousel-next',
20441                             cn : [
20442                                 {
20443                                     tag : 'i',
20444                                     class : 'fa fa-chevron-right'
20445                                 }
20446                             ]
20447                         }
20448                     ]
20449                 });
20450             }
20451             
20452         }
20453         
20454         return cfg;
20455     },
20456     
20457     initEvents:  function()
20458     {
20459 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20460 //            this.el.on("touchstart", this.onTouchStart, this);
20461 //        }
20462         
20463         if(this.autoslide){
20464             var _this = this;
20465             
20466             this.slideFn = window.setInterval(function() {
20467                 _this.showPanelNext();
20468             }, this.timer);
20469         }
20470         
20471         if(this.showarrow){
20472             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20473             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20474         }
20475         
20476         
20477     },
20478     
20479 //    onTouchStart : function(e, el, o)
20480 //    {
20481 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20482 //            return;
20483 //        }
20484 //        
20485 //        this.showPanelNext();
20486 //    },
20487     
20488     
20489     getChildContainer : function()
20490     {
20491         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20492     },
20493     
20494     /**
20495     * register a Navigation item
20496     * @param {Roo.bootstrap.NavItem} the navitem to add
20497     */
20498     register : function(item)
20499     {
20500         this.tabs.push( item);
20501         item.navId = this.navId; // not really needed..
20502         this.addBullet();
20503     
20504     },
20505     
20506     getActivePanel : function()
20507     {
20508         var r = false;
20509         Roo.each(this.tabs, function(t) {
20510             if (t.active) {
20511                 r = t;
20512                 return false;
20513             }
20514             return null;
20515         });
20516         return r;
20517         
20518     },
20519     getPanelByName : function(n)
20520     {
20521         var r = false;
20522         Roo.each(this.tabs, function(t) {
20523             if (t.tabId == n) {
20524                 r = t;
20525                 return false;
20526             }
20527             return null;
20528         });
20529         return r;
20530     },
20531     indexOfPanel : function(p)
20532     {
20533         var r = false;
20534         Roo.each(this.tabs, function(t,i) {
20535             if (t.tabId == p.tabId) {
20536                 r = i;
20537                 return false;
20538             }
20539             return null;
20540         });
20541         return r;
20542     },
20543     /**
20544      * show a specific panel
20545      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20546      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20547      */
20548     showPanel : function (pan)
20549     {
20550         if(this.transition || typeof(pan) == 'undefined'){
20551             Roo.log("waiting for the transitionend");
20552             return false;
20553         }
20554         
20555         if (typeof(pan) == 'number') {
20556             pan = this.tabs[pan];
20557         }
20558         
20559         if (typeof(pan) == 'string') {
20560             pan = this.getPanelByName(pan);
20561         }
20562         
20563         var cur = this.getActivePanel();
20564         
20565         if(!pan || !cur){
20566             Roo.log('pan or acitve pan is undefined');
20567             return false;
20568         }
20569         
20570         if (pan.tabId == this.getActivePanel().tabId) {
20571             return true;
20572         }
20573         
20574         if (false === cur.fireEvent('beforedeactivate')) {
20575             return false;
20576         }
20577         
20578         if(this.bullets > 0 && !Roo.isTouch){
20579             this.setActiveBullet(this.indexOfPanel(pan));
20580         }
20581         
20582         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20583             
20584             //class="carousel-item carousel-item-next carousel-item-left"
20585             
20586             this.transition = true;
20587             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20588             var lr = dir == 'next' ? 'left' : 'right';
20589             pan.el.addClass(dir); // or prev
20590             pan.el.addClass('carousel-item-' + dir); // or prev
20591             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20592             cur.el.addClass(lr); // or right
20593             pan.el.addClass(lr);
20594             cur.el.addClass('carousel-item-' +lr); // or right
20595             pan.el.addClass('carousel-item-' +lr);
20596             
20597             
20598             var _this = this;
20599             cur.el.on('transitionend', function() {
20600                 Roo.log("trans end?");
20601                 
20602                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20603                 pan.setActive(true);
20604                 
20605                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20606                 cur.setActive(false);
20607                 
20608                 _this.transition = false;
20609                 
20610             }, this, { single:  true } );
20611             
20612             return true;
20613         }
20614         
20615         cur.setActive(false);
20616         pan.setActive(true);
20617         
20618         return true;
20619         
20620     },
20621     showPanelNext : function()
20622     {
20623         var i = this.indexOfPanel(this.getActivePanel());
20624         
20625         if (i >= this.tabs.length - 1 && !this.autoslide) {
20626             return;
20627         }
20628         
20629         if (i >= this.tabs.length - 1 && this.autoslide) {
20630             i = -1;
20631         }
20632         
20633         this.showPanel(this.tabs[i+1]);
20634     },
20635     
20636     showPanelPrev : function()
20637     {
20638         var i = this.indexOfPanel(this.getActivePanel());
20639         
20640         if (i  < 1 && !this.autoslide) {
20641             return;
20642         }
20643         
20644         if (i < 1 && this.autoslide) {
20645             i = this.tabs.length;
20646         }
20647         
20648         this.showPanel(this.tabs[i-1]);
20649     },
20650     
20651     
20652     addBullet: function()
20653     {
20654         if(!this.bullets || Roo.isTouch){
20655             return;
20656         }
20657         var ctr = this.el.select('.carousel-bullets',true).first();
20658         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20659         var bullet = ctr.createChild({
20660             cls : 'bullet bullet-' + i
20661         },ctr.dom.lastChild);
20662         
20663         
20664         var _this = this;
20665         
20666         bullet.on('click', (function(e, el, o, ii, t){
20667
20668             e.preventDefault();
20669
20670             this.showPanel(ii);
20671
20672             if(this.autoslide && this.slideFn){
20673                 clearInterval(this.slideFn);
20674                 this.slideFn = window.setInterval(function() {
20675                     _this.showPanelNext();
20676                 }, this.timer);
20677             }
20678
20679         }).createDelegate(this, [i, bullet], true));
20680                 
20681         
20682     },
20683      
20684     setActiveBullet : function(i)
20685     {
20686         if(Roo.isTouch){
20687             return;
20688         }
20689         
20690         Roo.each(this.el.select('.bullet', true).elements, function(el){
20691             el.removeClass('selected');
20692         });
20693
20694         var bullet = this.el.select('.bullet-' + i, true).first();
20695         
20696         if(!bullet){
20697             return;
20698         }
20699         
20700         bullet.addClass('selected');
20701     }
20702     
20703     
20704   
20705 });
20706
20707  
20708
20709  
20710  
20711 Roo.apply(Roo.bootstrap.TabGroup, {
20712     
20713     groups: {},
20714      /**
20715     * register a Navigation Group
20716     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20717     */
20718     register : function(navgrp)
20719     {
20720         this.groups[navgrp.navId] = navgrp;
20721         
20722     },
20723     /**
20724     * fetch a Navigation Group based on the navigation ID
20725     * if one does not exist , it will get created.
20726     * @param {string} the navgroup to add
20727     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20728     */
20729     get: function(navId) {
20730         if (typeof(this.groups[navId]) == 'undefined') {
20731             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20732         }
20733         return this.groups[navId] ;
20734     }
20735     
20736     
20737     
20738 });
20739
20740  /*
20741  * - LGPL
20742  *
20743  * TabPanel
20744  * 
20745  */
20746
20747 /**
20748  * @class Roo.bootstrap.TabPanel
20749  * @extends Roo.bootstrap.Component
20750  * Bootstrap TabPanel class
20751  * @cfg {Boolean} active panel active
20752  * @cfg {String} html panel content
20753  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20754  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20755  * @cfg {String} href click to link..
20756  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20757  * 
20758  * 
20759  * @constructor
20760  * Create a new TabPanel
20761  * @param {Object} config The config object
20762  */
20763
20764 Roo.bootstrap.TabPanel = function(config){
20765     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20766     this.addEvents({
20767         /**
20768              * @event changed
20769              * Fires when the active status changes
20770              * @param {Roo.bootstrap.TabPanel} this
20771              * @param {Boolean} state the new state
20772             
20773          */
20774         'changed': true,
20775         /**
20776              * @event beforedeactivate
20777              * Fires before a tab is de-activated - can be used to do validation on a form.
20778              * @param {Roo.bootstrap.TabPanel} this
20779              * @return {Boolean} false if there is an error
20780             
20781          */
20782         'beforedeactivate': true
20783      });
20784     
20785     this.tabId = this.tabId || Roo.id();
20786   
20787 };
20788
20789 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20790     
20791     active: false,
20792     html: false,
20793     tabId: false,
20794     navId : false,
20795     href : '',
20796     touchSlide : false,
20797     getAutoCreate : function(){
20798         
20799         
20800         var cfg = {
20801             tag: 'div',
20802             // item is needed for carousel - not sure if it has any effect otherwise
20803             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20804             html: this.html || ''
20805         };
20806         
20807         if(this.active){
20808             cfg.cls += ' active';
20809         }
20810         
20811         if(this.tabId){
20812             cfg.tabId = this.tabId;
20813         }
20814         
20815         
20816         
20817         return cfg;
20818     },
20819     
20820     initEvents:  function()
20821     {
20822         var p = this.parent();
20823         
20824         this.navId = this.navId || p.navId;
20825         
20826         if (typeof(this.navId) != 'undefined') {
20827             // not really needed.. but just in case.. parent should be a NavGroup.
20828             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20829             
20830             tg.register(this);
20831             
20832             var i = tg.tabs.length - 1;
20833             
20834             if(this.active && tg.bullets > 0 && i < tg.bullets){
20835                 tg.setActiveBullet(i);
20836             }
20837         }
20838         
20839         this.el.on('click', this.onClick, this);
20840         
20841         if(Roo.isTouch && this.touchSlide){
20842             this.el.on("touchstart", this.onTouchStart, this);
20843             this.el.on("touchmove", this.onTouchMove, this);
20844             this.el.on("touchend", this.onTouchEnd, this);
20845         }
20846         
20847     },
20848     
20849     onRender : function(ct, position)
20850     {
20851         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20852     },
20853     
20854     setActive : function(state)
20855     {
20856         Roo.log("panel - set active " + this.tabId + "=" + state);
20857         
20858         this.active = state;
20859         if (!state) {
20860             this.el.removeClass('active');
20861             
20862         } else  if (!this.el.hasClass('active')) {
20863             this.el.addClass('active');
20864         }
20865         
20866         this.fireEvent('changed', this, state);
20867     },
20868     
20869     onClick : function(e)
20870     {
20871         e.preventDefault();
20872         
20873         if(!this.href.length){
20874             return;
20875         }
20876         
20877         window.location.href = this.href;
20878     },
20879     
20880     startX : 0,
20881     startY : 0,
20882     endX : 0,
20883     endY : 0,
20884     swiping : false,
20885     
20886     onTouchStart : function(e)
20887     {
20888         this.swiping = false;
20889         
20890         this.startX = e.browserEvent.touches[0].clientX;
20891         this.startY = e.browserEvent.touches[0].clientY;
20892     },
20893     
20894     onTouchMove : function(e)
20895     {
20896         this.swiping = true;
20897         
20898         this.endX = e.browserEvent.touches[0].clientX;
20899         this.endY = e.browserEvent.touches[0].clientY;
20900     },
20901     
20902     onTouchEnd : function(e)
20903     {
20904         if(!this.swiping){
20905             this.onClick(e);
20906             return;
20907         }
20908         
20909         var tabGroup = this.parent();
20910         
20911         if(this.endX > this.startX){ // swiping right
20912             tabGroup.showPanelPrev();
20913             return;
20914         }
20915         
20916         if(this.startX > this.endX){ // swiping left
20917             tabGroup.showPanelNext();
20918             return;
20919         }
20920     }
20921     
20922     
20923 });
20924  
20925
20926  
20927
20928  /*
20929  * - LGPL
20930  *
20931  * DateField
20932  * 
20933  */
20934
20935 /**
20936  * @class Roo.bootstrap.DateField
20937  * @extends Roo.bootstrap.Input
20938  * Bootstrap DateField class
20939  * @cfg {Number} weekStart default 0
20940  * @cfg {String} viewMode default empty, (months|years)
20941  * @cfg {String} minViewMode default empty, (months|years)
20942  * @cfg {Number} startDate default -Infinity
20943  * @cfg {Number} endDate default Infinity
20944  * @cfg {Boolean} todayHighlight default false
20945  * @cfg {Boolean} todayBtn default false
20946  * @cfg {Boolean} calendarWeeks default false
20947  * @cfg {Object} daysOfWeekDisabled default empty
20948  * @cfg {Boolean} singleMode default false (true | false)
20949  * 
20950  * @cfg {Boolean} keyboardNavigation default true
20951  * @cfg {String} language default en
20952  * 
20953  * @constructor
20954  * Create a new DateField
20955  * @param {Object} config The config object
20956  */
20957
20958 Roo.bootstrap.DateField = function(config){
20959     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20960      this.addEvents({
20961             /**
20962              * @event show
20963              * Fires when this field show.
20964              * @param {Roo.bootstrap.DateField} this
20965              * @param {Mixed} date The date value
20966              */
20967             show : true,
20968             /**
20969              * @event show
20970              * Fires when this field hide.
20971              * @param {Roo.bootstrap.DateField} this
20972              * @param {Mixed} date The date value
20973              */
20974             hide : true,
20975             /**
20976              * @event select
20977              * Fires when select a date.
20978              * @param {Roo.bootstrap.DateField} this
20979              * @param {Mixed} date The date value
20980              */
20981             select : true,
20982             /**
20983              * @event beforeselect
20984              * Fires when before select a date.
20985              * @param {Roo.bootstrap.DateField} this
20986              * @param {Mixed} date The date value
20987              */
20988             beforeselect : true
20989         });
20990 };
20991
20992 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20993     
20994     /**
20995      * @cfg {String} format
20996      * The default date format string which can be overriden for localization support.  The format must be
20997      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20998      */
20999     format : "m/d/y",
21000     /**
21001      * @cfg {String} altFormats
21002      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
21003      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
21004      */
21005     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
21006     
21007     weekStart : 0,
21008     
21009     viewMode : '',
21010     
21011     minViewMode : '',
21012     
21013     todayHighlight : false,
21014     
21015     todayBtn: false,
21016     
21017     language: 'en',
21018     
21019     keyboardNavigation: true,
21020     
21021     calendarWeeks: false,
21022     
21023     startDate: -Infinity,
21024     
21025     endDate: Infinity,
21026     
21027     daysOfWeekDisabled: [],
21028     
21029     _events: [],
21030     
21031     singleMode : false,
21032     
21033     UTCDate: function()
21034     {
21035         return new Date(Date.UTC.apply(Date, arguments));
21036     },
21037     
21038     UTCToday: function()
21039     {
21040         var today = new Date();
21041         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
21042     },
21043     
21044     getDate: function() {
21045             var d = this.getUTCDate();
21046             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
21047     },
21048     
21049     getUTCDate: function() {
21050             return this.date;
21051     },
21052     
21053     setDate: function(d) {
21054             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21055     },
21056     
21057     setUTCDate: function(d) {
21058             this.date = d;
21059             this.setValue(this.formatDate(this.date));
21060     },
21061         
21062     onRender: function(ct, position)
21063     {
21064         
21065         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21066         
21067         this.language = this.language || 'en';
21068         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21069         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21070         
21071         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21072         this.format = this.format || 'm/d/y';
21073         this.isInline = false;
21074         this.isInput = true;
21075         this.component = this.el.select('.add-on', true).first() || false;
21076         this.component = (this.component && this.component.length === 0) ? false : this.component;
21077         this.hasInput = this.component && this.inputEl().length;
21078         
21079         if (typeof(this.minViewMode === 'string')) {
21080             switch (this.minViewMode) {
21081                 case 'months':
21082                     this.minViewMode = 1;
21083                     break;
21084                 case 'years':
21085                     this.minViewMode = 2;
21086                     break;
21087                 default:
21088                     this.minViewMode = 0;
21089                     break;
21090             }
21091         }
21092         
21093         if (typeof(this.viewMode === 'string')) {
21094             switch (this.viewMode) {
21095                 case 'months':
21096                     this.viewMode = 1;
21097                     break;
21098                 case 'years':
21099                     this.viewMode = 2;
21100                     break;
21101                 default:
21102                     this.viewMode = 0;
21103                     break;
21104             }
21105         }
21106                 
21107         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21108         
21109 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21110         
21111         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21112         
21113         this.picker().on('mousedown', this.onMousedown, this);
21114         this.picker().on('click', this.onClick, this);
21115         
21116         this.picker().addClass('datepicker-dropdown');
21117         
21118         this.startViewMode = this.viewMode;
21119         
21120         if(this.singleMode){
21121             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21122                 v.setVisibilityMode(Roo.Element.DISPLAY);
21123                 v.hide();
21124             });
21125             
21126             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21127                 v.setStyle('width', '189px');
21128             });
21129         }
21130         
21131         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21132             if(!this.calendarWeeks){
21133                 v.remove();
21134                 return;
21135             }
21136             
21137             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21138             v.attr('colspan', function(i, val){
21139                 return parseInt(val) + 1;
21140             });
21141         });
21142                         
21143         
21144         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21145         
21146         this.setStartDate(this.startDate);
21147         this.setEndDate(this.endDate);
21148         
21149         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21150         
21151         this.fillDow();
21152         this.fillMonths();
21153         this.update();
21154         this.showMode();
21155         
21156         if(this.isInline) {
21157             this.showPopup();
21158         }
21159     },
21160     
21161     picker : function()
21162     {
21163         return this.pickerEl;
21164 //        return this.el.select('.datepicker', true).first();
21165     },
21166     
21167     fillDow: function()
21168     {
21169         var dowCnt = this.weekStart;
21170         
21171         var dow = {
21172             tag: 'tr',
21173             cn: [
21174                 
21175             ]
21176         };
21177         
21178         if(this.calendarWeeks){
21179             dow.cn.push({
21180                 tag: 'th',
21181                 cls: 'cw',
21182                 html: '&nbsp;'
21183             })
21184         }
21185         
21186         while (dowCnt < this.weekStart + 7) {
21187             dow.cn.push({
21188                 tag: 'th',
21189                 cls: 'dow',
21190                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21191             });
21192         }
21193         
21194         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21195     },
21196     
21197     fillMonths: function()
21198     {    
21199         var i = 0;
21200         var months = this.picker().select('>.datepicker-months td', true).first();
21201         
21202         months.dom.innerHTML = '';
21203         
21204         while (i < 12) {
21205             var month = {
21206                 tag: 'span',
21207                 cls: 'month',
21208                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21209             };
21210             
21211             months.createChild(month);
21212         }
21213         
21214     },
21215     
21216     update: function()
21217     {
21218         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;
21219         
21220         if (this.date < this.startDate) {
21221             this.viewDate = new Date(this.startDate);
21222         } else if (this.date > this.endDate) {
21223             this.viewDate = new Date(this.endDate);
21224         } else {
21225             this.viewDate = new Date(this.date);
21226         }
21227         
21228         this.fill();
21229     },
21230     
21231     fill: function() 
21232     {
21233         var d = new Date(this.viewDate),
21234                 year = d.getUTCFullYear(),
21235                 month = d.getUTCMonth(),
21236                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21237                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21238                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21239                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21240                 currentDate = this.date && this.date.valueOf(),
21241                 today = this.UTCToday();
21242         
21243         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21244         
21245 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21246         
21247 //        this.picker.select('>tfoot th.today').
21248 //                                              .text(dates[this.language].today)
21249 //                                              .toggle(this.todayBtn !== false);
21250     
21251         this.updateNavArrows();
21252         this.fillMonths();
21253                                                 
21254         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21255         
21256         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21257          
21258         prevMonth.setUTCDate(day);
21259         
21260         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21261         
21262         var nextMonth = new Date(prevMonth);
21263         
21264         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21265         
21266         nextMonth = nextMonth.valueOf();
21267         
21268         var fillMonths = false;
21269         
21270         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21271         
21272         while(prevMonth.valueOf() <= nextMonth) {
21273             var clsName = '';
21274             
21275             if (prevMonth.getUTCDay() === this.weekStart) {
21276                 if(fillMonths){
21277                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21278                 }
21279                     
21280                 fillMonths = {
21281                     tag: 'tr',
21282                     cn: []
21283                 };
21284                 
21285                 if(this.calendarWeeks){
21286                     // ISO 8601: First week contains first thursday.
21287                     // ISO also states week starts on Monday, but we can be more abstract here.
21288                     var
21289                     // Start of current week: based on weekstart/current date
21290                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21291                     // Thursday of this week
21292                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21293                     // First Thursday of year, year from thursday
21294                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21295                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21296                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21297                     
21298                     fillMonths.cn.push({
21299                         tag: 'td',
21300                         cls: 'cw',
21301                         html: calWeek
21302                     });
21303                 }
21304             }
21305             
21306             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21307                 clsName += ' old';
21308             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21309                 clsName += ' new';
21310             }
21311             if (this.todayHighlight &&
21312                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21313                 prevMonth.getUTCMonth() == today.getMonth() &&
21314                 prevMonth.getUTCDate() == today.getDate()) {
21315                 clsName += ' today';
21316             }
21317             
21318             if (currentDate && prevMonth.valueOf() === currentDate) {
21319                 clsName += ' active';
21320             }
21321             
21322             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21323                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21324                     clsName += ' disabled';
21325             }
21326             
21327             fillMonths.cn.push({
21328                 tag: 'td',
21329                 cls: 'day ' + clsName,
21330                 html: prevMonth.getDate()
21331             });
21332             
21333             prevMonth.setDate(prevMonth.getDate()+1);
21334         }
21335           
21336         var currentYear = this.date && this.date.getUTCFullYear();
21337         var currentMonth = this.date && this.date.getUTCMonth();
21338         
21339         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21340         
21341         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21342             v.removeClass('active');
21343             
21344             if(currentYear === year && k === currentMonth){
21345                 v.addClass('active');
21346             }
21347             
21348             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21349                 v.addClass('disabled');
21350             }
21351             
21352         });
21353         
21354         
21355         year = parseInt(year/10, 10) * 10;
21356         
21357         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21358         
21359         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21360         
21361         year -= 1;
21362         for (var i = -1; i < 11; i++) {
21363             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21364                 tag: 'span',
21365                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21366                 html: year
21367             });
21368             
21369             year += 1;
21370         }
21371     },
21372     
21373     showMode: function(dir) 
21374     {
21375         if (dir) {
21376             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21377         }
21378         
21379         Roo.each(this.picker().select('>div',true).elements, function(v){
21380             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21381             v.hide();
21382         });
21383         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21384     },
21385     
21386     place: function()
21387     {
21388         if(this.isInline) {
21389             return;
21390         }
21391         
21392         this.picker().removeClass(['bottom', 'top']);
21393         
21394         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21395             /*
21396              * place to the top of element!
21397              *
21398              */
21399             
21400             this.picker().addClass('top');
21401             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21402             
21403             return;
21404         }
21405         
21406         this.picker().addClass('bottom');
21407         
21408         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21409     },
21410     
21411     parseDate : function(value)
21412     {
21413         if(!value || value instanceof Date){
21414             return value;
21415         }
21416         var v = Date.parseDate(value, this.format);
21417         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21418             v = Date.parseDate(value, 'Y-m-d');
21419         }
21420         if(!v && this.altFormats){
21421             if(!this.altFormatsArray){
21422                 this.altFormatsArray = this.altFormats.split("|");
21423             }
21424             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21425                 v = Date.parseDate(value, this.altFormatsArray[i]);
21426             }
21427         }
21428         return v;
21429     },
21430     
21431     formatDate : function(date, fmt)
21432     {   
21433         return (!date || !(date instanceof Date)) ?
21434         date : date.dateFormat(fmt || this.format);
21435     },
21436     
21437     onFocus : function()
21438     {
21439         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21440         this.showPopup();
21441     },
21442     
21443     onBlur : function()
21444     {
21445         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21446         
21447         var d = this.inputEl().getValue();
21448         
21449         this.setValue(d);
21450                 
21451         this.hidePopup();
21452     },
21453     
21454     showPopup : function()
21455     {
21456         this.picker().show();
21457         this.update();
21458         this.place();
21459         
21460         this.fireEvent('showpopup', this, this.date);
21461     },
21462     
21463     hidePopup : function()
21464     {
21465         if(this.isInline) {
21466             return;
21467         }
21468         this.picker().hide();
21469         this.viewMode = this.startViewMode;
21470         this.showMode();
21471         
21472         this.fireEvent('hidepopup', this, this.date);
21473         
21474     },
21475     
21476     onMousedown: function(e)
21477     {
21478         e.stopPropagation();
21479         e.preventDefault();
21480     },
21481     
21482     keyup: function(e)
21483     {
21484         Roo.bootstrap.DateField.superclass.keyup.call(this);
21485         this.update();
21486     },
21487
21488     setValue: function(v)
21489     {
21490         if(this.fireEvent('beforeselect', this, v) !== false){
21491             var d = new Date(this.parseDate(v) ).clearTime();
21492         
21493             if(isNaN(d.getTime())){
21494                 this.date = this.viewDate = '';
21495                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21496                 return;
21497             }
21498
21499             v = this.formatDate(d);
21500
21501             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21502
21503             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21504
21505             this.update();
21506
21507             this.fireEvent('select', this, this.date);
21508         }
21509     },
21510     
21511     getValue: function()
21512     {
21513         return this.formatDate(this.date);
21514     },
21515     
21516     fireKey: function(e)
21517     {
21518         if (!this.picker().isVisible()){
21519             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21520                 this.showPopup();
21521             }
21522             return;
21523         }
21524         
21525         var dateChanged = false,
21526         dir, day, month,
21527         newDate, newViewDate;
21528         
21529         switch(e.keyCode){
21530             case 27: // escape
21531                 this.hidePopup();
21532                 e.preventDefault();
21533                 break;
21534             case 37: // left
21535             case 39: // right
21536                 if (!this.keyboardNavigation) {
21537                     break;
21538                 }
21539                 dir = e.keyCode == 37 ? -1 : 1;
21540                 
21541                 if (e.ctrlKey){
21542                     newDate = this.moveYear(this.date, dir);
21543                     newViewDate = this.moveYear(this.viewDate, dir);
21544                 } else if (e.shiftKey){
21545                     newDate = this.moveMonth(this.date, dir);
21546                     newViewDate = this.moveMonth(this.viewDate, dir);
21547                 } else {
21548                     newDate = new Date(this.date);
21549                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21550                     newViewDate = new Date(this.viewDate);
21551                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21552                 }
21553                 if (this.dateWithinRange(newDate)){
21554                     this.date = newDate;
21555                     this.viewDate = newViewDate;
21556                     this.setValue(this.formatDate(this.date));
21557 //                    this.update();
21558                     e.preventDefault();
21559                     dateChanged = true;
21560                 }
21561                 break;
21562             case 38: // up
21563             case 40: // down
21564                 if (!this.keyboardNavigation) {
21565                     break;
21566                 }
21567                 dir = e.keyCode == 38 ? -1 : 1;
21568                 if (e.ctrlKey){
21569                     newDate = this.moveYear(this.date, dir);
21570                     newViewDate = this.moveYear(this.viewDate, dir);
21571                 } else if (e.shiftKey){
21572                     newDate = this.moveMonth(this.date, dir);
21573                     newViewDate = this.moveMonth(this.viewDate, dir);
21574                 } else {
21575                     newDate = new Date(this.date);
21576                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21577                     newViewDate = new Date(this.viewDate);
21578                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21579                 }
21580                 if (this.dateWithinRange(newDate)){
21581                     this.date = newDate;
21582                     this.viewDate = newViewDate;
21583                     this.setValue(this.formatDate(this.date));
21584 //                    this.update();
21585                     e.preventDefault();
21586                     dateChanged = true;
21587                 }
21588                 break;
21589             case 13: // enter
21590                 this.setValue(this.formatDate(this.date));
21591                 this.hidePopup();
21592                 e.preventDefault();
21593                 break;
21594             case 9: // tab
21595                 this.setValue(this.formatDate(this.date));
21596                 this.hidePopup();
21597                 break;
21598             case 16: // shift
21599             case 17: // ctrl
21600             case 18: // alt
21601                 break;
21602             default :
21603                 this.hidePopup();
21604                 
21605         }
21606     },
21607     
21608     
21609     onClick: function(e) 
21610     {
21611         e.stopPropagation();
21612         e.preventDefault();
21613         
21614         var target = e.getTarget();
21615         
21616         if(target.nodeName.toLowerCase() === 'i'){
21617             target = Roo.get(target).dom.parentNode;
21618         }
21619         
21620         var nodeName = target.nodeName;
21621         var className = target.className;
21622         var html = target.innerHTML;
21623         //Roo.log(nodeName);
21624         
21625         switch(nodeName.toLowerCase()) {
21626             case 'th':
21627                 switch(className) {
21628                     case 'switch':
21629                         this.showMode(1);
21630                         break;
21631                     case 'prev':
21632                     case 'next':
21633                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21634                         switch(this.viewMode){
21635                                 case 0:
21636                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21637                                         break;
21638                                 case 1:
21639                                 case 2:
21640                                         this.viewDate = this.moveYear(this.viewDate, dir);
21641                                         break;
21642                         }
21643                         this.fill();
21644                         break;
21645                     case 'today':
21646                         var date = new Date();
21647                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21648 //                        this.fill()
21649                         this.setValue(this.formatDate(this.date));
21650                         
21651                         this.hidePopup();
21652                         break;
21653                 }
21654                 break;
21655             case 'span':
21656                 if (className.indexOf('disabled') < 0) {
21657                     this.viewDate.setUTCDate(1);
21658                     if (className.indexOf('month') > -1) {
21659                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21660                     } else {
21661                         var year = parseInt(html, 10) || 0;
21662                         this.viewDate.setUTCFullYear(year);
21663                         
21664                     }
21665                     
21666                     if(this.singleMode){
21667                         this.setValue(this.formatDate(this.viewDate));
21668                         this.hidePopup();
21669                         return;
21670                     }
21671                     
21672                     this.showMode(-1);
21673                     this.fill();
21674                 }
21675                 break;
21676                 
21677             case 'td':
21678                 //Roo.log(className);
21679                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21680                     var day = parseInt(html, 10) || 1;
21681                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21682                         month = (this.viewDate || new Date()).getUTCMonth();
21683
21684                     if (className.indexOf('old') > -1) {
21685                         if(month === 0 ){
21686                             month = 11;
21687                             year -= 1;
21688                         }else{
21689                             month -= 1;
21690                         }
21691                     } else if (className.indexOf('new') > -1) {
21692                         if (month == 11) {
21693                             month = 0;
21694                             year += 1;
21695                         } else {
21696                             month += 1;
21697                         }
21698                     }
21699                     //Roo.log([year,month,day]);
21700                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21701                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21702 //                    this.fill();
21703                     //Roo.log(this.formatDate(this.date));
21704                     this.setValue(this.formatDate(this.date));
21705                     this.hidePopup();
21706                 }
21707                 break;
21708         }
21709     },
21710     
21711     setStartDate: function(startDate)
21712     {
21713         this.startDate = startDate || -Infinity;
21714         if (this.startDate !== -Infinity) {
21715             this.startDate = this.parseDate(this.startDate);
21716         }
21717         this.update();
21718         this.updateNavArrows();
21719     },
21720
21721     setEndDate: function(endDate)
21722     {
21723         this.endDate = endDate || Infinity;
21724         if (this.endDate !== Infinity) {
21725             this.endDate = this.parseDate(this.endDate);
21726         }
21727         this.update();
21728         this.updateNavArrows();
21729     },
21730     
21731     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21732     {
21733         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21734         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21735             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21736         }
21737         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21738             return parseInt(d, 10);
21739         });
21740         this.update();
21741         this.updateNavArrows();
21742     },
21743     
21744     updateNavArrows: function() 
21745     {
21746         if(this.singleMode){
21747             return;
21748         }
21749         
21750         var d = new Date(this.viewDate),
21751         year = d.getUTCFullYear(),
21752         month = d.getUTCMonth();
21753         
21754         Roo.each(this.picker().select('.prev', true).elements, function(v){
21755             v.show();
21756             switch (this.viewMode) {
21757                 case 0:
21758
21759                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21760                         v.hide();
21761                     }
21762                     break;
21763                 case 1:
21764                 case 2:
21765                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21766                         v.hide();
21767                     }
21768                     break;
21769             }
21770         });
21771         
21772         Roo.each(this.picker().select('.next', true).elements, function(v){
21773             v.show();
21774             switch (this.viewMode) {
21775                 case 0:
21776
21777                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21778                         v.hide();
21779                     }
21780                     break;
21781                 case 1:
21782                 case 2:
21783                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21784                         v.hide();
21785                     }
21786                     break;
21787             }
21788         })
21789     },
21790     
21791     moveMonth: function(date, dir)
21792     {
21793         if (!dir) {
21794             return date;
21795         }
21796         var new_date = new Date(date.valueOf()),
21797         day = new_date.getUTCDate(),
21798         month = new_date.getUTCMonth(),
21799         mag = Math.abs(dir),
21800         new_month, test;
21801         dir = dir > 0 ? 1 : -1;
21802         if (mag == 1){
21803             test = dir == -1
21804             // If going back one month, make sure month is not current month
21805             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21806             ? function(){
21807                 return new_date.getUTCMonth() == month;
21808             }
21809             // If going forward one month, make sure month is as expected
21810             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21811             : function(){
21812                 return new_date.getUTCMonth() != new_month;
21813             };
21814             new_month = month + dir;
21815             new_date.setUTCMonth(new_month);
21816             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21817             if (new_month < 0 || new_month > 11) {
21818                 new_month = (new_month + 12) % 12;
21819             }
21820         } else {
21821             // For magnitudes >1, move one month at a time...
21822             for (var i=0; i<mag; i++) {
21823                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21824                 new_date = this.moveMonth(new_date, dir);
21825             }
21826             // ...then reset the day, keeping it in the new month
21827             new_month = new_date.getUTCMonth();
21828             new_date.setUTCDate(day);
21829             test = function(){
21830                 return new_month != new_date.getUTCMonth();
21831             };
21832         }
21833         // Common date-resetting loop -- if date is beyond end of month, make it
21834         // end of month
21835         while (test()){
21836             new_date.setUTCDate(--day);
21837             new_date.setUTCMonth(new_month);
21838         }
21839         return new_date;
21840     },
21841
21842     moveYear: function(date, dir)
21843     {
21844         return this.moveMonth(date, dir*12);
21845     },
21846
21847     dateWithinRange: function(date)
21848     {
21849         return date >= this.startDate && date <= this.endDate;
21850     },
21851
21852     
21853     remove: function() 
21854     {
21855         this.picker().remove();
21856     },
21857     
21858     validateValue : function(value)
21859     {
21860         if(this.getVisibilityEl().hasClass('hidden')){
21861             return true;
21862         }
21863         
21864         if(value.length < 1)  {
21865             if(this.allowBlank){
21866                 return true;
21867             }
21868             return false;
21869         }
21870         
21871         if(value.length < this.minLength){
21872             return false;
21873         }
21874         if(value.length > this.maxLength){
21875             return false;
21876         }
21877         if(this.vtype){
21878             var vt = Roo.form.VTypes;
21879             if(!vt[this.vtype](value, this)){
21880                 return false;
21881             }
21882         }
21883         if(typeof this.validator == "function"){
21884             var msg = this.validator(value);
21885             if(msg !== true){
21886                 return false;
21887             }
21888         }
21889         
21890         if(this.regex && !this.regex.test(value)){
21891             return false;
21892         }
21893         
21894         if(typeof(this.parseDate(value)) == 'undefined'){
21895             return false;
21896         }
21897         
21898         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21899             return false;
21900         }      
21901         
21902         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21903             return false;
21904         } 
21905         
21906         
21907         return true;
21908     },
21909     
21910     reset : function()
21911     {
21912         this.date = this.viewDate = '';
21913         
21914         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21915     }
21916    
21917 });
21918
21919 Roo.apply(Roo.bootstrap.DateField,  {
21920     
21921     head : {
21922         tag: 'thead',
21923         cn: [
21924         {
21925             tag: 'tr',
21926             cn: [
21927             {
21928                 tag: 'th',
21929                 cls: 'prev',
21930                 html: '<i class="fa fa-arrow-left"/>'
21931             },
21932             {
21933                 tag: 'th',
21934                 cls: 'switch',
21935                 colspan: '5'
21936             },
21937             {
21938                 tag: 'th',
21939                 cls: 'next',
21940                 html: '<i class="fa fa-arrow-right"/>'
21941             }
21942
21943             ]
21944         }
21945         ]
21946     },
21947     
21948     content : {
21949         tag: 'tbody',
21950         cn: [
21951         {
21952             tag: 'tr',
21953             cn: [
21954             {
21955                 tag: 'td',
21956                 colspan: '7'
21957             }
21958             ]
21959         }
21960         ]
21961     },
21962     
21963     footer : {
21964         tag: 'tfoot',
21965         cn: [
21966         {
21967             tag: 'tr',
21968             cn: [
21969             {
21970                 tag: 'th',
21971                 colspan: '7',
21972                 cls: 'today'
21973             }
21974                     
21975             ]
21976         }
21977         ]
21978     },
21979     
21980     dates:{
21981         en: {
21982             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21983             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21984             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21985             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21986             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21987             today: "Today"
21988         }
21989     },
21990     
21991     modes: [
21992     {
21993         clsName: 'days',
21994         navFnc: 'Month',
21995         navStep: 1
21996     },
21997     {
21998         clsName: 'months',
21999         navFnc: 'FullYear',
22000         navStep: 1
22001     },
22002     {
22003         clsName: 'years',
22004         navFnc: 'FullYear',
22005         navStep: 10
22006     }]
22007 });
22008
22009 Roo.apply(Roo.bootstrap.DateField,  {
22010   
22011     template : {
22012         tag: 'div',
22013         cls: 'datepicker dropdown-menu roo-dynamic shadow',
22014         cn: [
22015         {
22016             tag: 'div',
22017             cls: 'datepicker-days',
22018             cn: [
22019             {
22020                 tag: 'table',
22021                 cls: 'table-condensed',
22022                 cn:[
22023                 Roo.bootstrap.DateField.head,
22024                 {
22025                     tag: 'tbody'
22026                 },
22027                 Roo.bootstrap.DateField.footer
22028                 ]
22029             }
22030             ]
22031         },
22032         {
22033             tag: 'div',
22034             cls: 'datepicker-months',
22035             cn: [
22036             {
22037                 tag: 'table',
22038                 cls: 'table-condensed',
22039                 cn:[
22040                 Roo.bootstrap.DateField.head,
22041                 Roo.bootstrap.DateField.content,
22042                 Roo.bootstrap.DateField.footer
22043                 ]
22044             }
22045             ]
22046         },
22047         {
22048             tag: 'div',
22049             cls: 'datepicker-years',
22050             cn: [
22051             {
22052                 tag: 'table',
22053                 cls: 'table-condensed',
22054                 cn:[
22055                 Roo.bootstrap.DateField.head,
22056                 Roo.bootstrap.DateField.content,
22057                 Roo.bootstrap.DateField.footer
22058                 ]
22059             }
22060             ]
22061         }
22062         ]
22063     }
22064 });
22065
22066  
22067
22068  /*
22069  * - LGPL
22070  *
22071  * TimeField
22072  * 
22073  */
22074
22075 /**
22076  * @class Roo.bootstrap.TimeField
22077  * @extends Roo.bootstrap.Input
22078  * Bootstrap DateField class
22079  * 
22080  * 
22081  * @constructor
22082  * Create a new TimeField
22083  * @param {Object} config The config object
22084  */
22085
22086 Roo.bootstrap.TimeField = function(config){
22087     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22088     this.addEvents({
22089             /**
22090              * @event show
22091              * Fires when this field show.
22092              * @param {Roo.bootstrap.DateField} thisthis
22093              * @param {Mixed} date The date value
22094              */
22095             show : true,
22096             /**
22097              * @event show
22098              * Fires when this field hide.
22099              * @param {Roo.bootstrap.DateField} this
22100              * @param {Mixed} date The date value
22101              */
22102             hide : true,
22103             /**
22104              * @event select
22105              * Fires when select a date.
22106              * @param {Roo.bootstrap.DateField} this
22107              * @param {Mixed} date The date value
22108              */
22109             select : true
22110         });
22111 };
22112
22113 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22114     
22115     /**
22116      * @cfg {String} format
22117      * The default time format string which can be overriden for localization support.  The format must be
22118      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22119      */
22120     format : "H:i",
22121
22122     getAutoCreate : function()
22123     {
22124         this.after = '<i class="fa far fa-clock"></i>';
22125         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22126         
22127          
22128     },
22129     onRender: function(ct, position)
22130     {
22131         
22132         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22133                 
22134         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22135         
22136         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22137         
22138         this.pop = this.picker().select('>.datepicker-time',true).first();
22139         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22140         
22141         this.picker().on('mousedown', this.onMousedown, this);
22142         this.picker().on('click', this.onClick, this);
22143         
22144         this.picker().addClass('datepicker-dropdown');
22145     
22146         this.fillTime();
22147         this.update();
22148             
22149         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22150         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22151         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22152         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22153         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22154         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22155
22156     },
22157     
22158     fireKey: function(e){
22159         if (!this.picker().isVisible()){
22160             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22161                 this.show();
22162             }
22163             return;
22164         }
22165
22166         e.preventDefault();
22167         
22168         switch(e.keyCode){
22169             case 27: // escape
22170                 this.hide();
22171                 break;
22172             case 37: // left
22173             case 39: // right
22174                 this.onTogglePeriod();
22175                 break;
22176             case 38: // up
22177                 this.onIncrementMinutes();
22178                 break;
22179             case 40: // down
22180                 this.onDecrementMinutes();
22181                 break;
22182             case 13: // enter
22183             case 9: // tab
22184                 this.setTime();
22185                 break;
22186         }
22187     },
22188     
22189     onClick: function(e) {
22190         e.stopPropagation();
22191         e.preventDefault();
22192     },
22193     
22194     picker : function()
22195     {
22196         return this.pickerEl;
22197     },
22198     
22199     fillTime: function()
22200     {    
22201         var time = this.pop.select('tbody', true).first();
22202         
22203         time.dom.innerHTML = '';
22204         
22205         time.createChild({
22206             tag: 'tr',
22207             cn: [
22208                 {
22209                     tag: 'td',
22210                     cn: [
22211                         {
22212                             tag: 'a',
22213                             href: '#',
22214                             cls: 'btn',
22215                             cn: [
22216                                 {
22217                                     tag: 'i',
22218                                     cls: 'hours-up fa fas fa-chevron-up'
22219                                 }
22220                             ]
22221                         } 
22222                     ]
22223                 },
22224                 {
22225                     tag: 'td',
22226                     cls: 'separator'
22227                 },
22228                 {
22229                     tag: 'td',
22230                     cn: [
22231                         {
22232                             tag: 'a',
22233                             href: '#',
22234                             cls: 'btn',
22235                             cn: [
22236                                 {
22237                                     tag: 'i',
22238                                     cls: 'minutes-up fa fas fa-chevron-up'
22239                                 }
22240                             ]
22241                         }
22242                     ]
22243                 },
22244                 {
22245                     tag: 'td',
22246                     cls: 'separator'
22247                 }
22248             ]
22249         });
22250         
22251         time.createChild({
22252             tag: 'tr',
22253             cn: [
22254                 {
22255                     tag: 'td',
22256                     cn: [
22257                         {
22258                             tag: 'span',
22259                             cls: 'timepicker-hour',
22260                             html: '00'
22261                         }  
22262                     ]
22263                 },
22264                 {
22265                     tag: 'td',
22266                     cls: 'separator',
22267                     html: ':'
22268                 },
22269                 {
22270                     tag: 'td',
22271                     cn: [
22272                         {
22273                             tag: 'span',
22274                             cls: 'timepicker-minute',
22275                             html: '00'
22276                         }  
22277                     ]
22278                 },
22279                 {
22280                     tag: 'td',
22281                     cls: 'separator'
22282                 },
22283                 {
22284                     tag: 'td',
22285                     cn: [
22286                         {
22287                             tag: 'button',
22288                             type: 'button',
22289                             cls: 'btn btn-primary period',
22290                             html: 'AM'
22291                             
22292                         }
22293                     ]
22294                 }
22295             ]
22296         });
22297         
22298         time.createChild({
22299             tag: 'tr',
22300             cn: [
22301                 {
22302                     tag: 'td',
22303                     cn: [
22304                         {
22305                             tag: 'a',
22306                             href: '#',
22307                             cls: 'btn',
22308                             cn: [
22309                                 {
22310                                     tag: 'span',
22311                                     cls: 'hours-down fa fas fa-chevron-down'
22312                                 }
22313                             ]
22314                         }
22315                     ]
22316                 },
22317                 {
22318                     tag: 'td',
22319                     cls: 'separator'
22320                 },
22321                 {
22322                     tag: 'td',
22323                     cn: [
22324                         {
22325                             tag: 'a',
22326                             href: '#',
22327                             cls: 'btn',
22328                             cn: [
22329                                 {
22330                                     tag: 'span',
22331                                     cls: 'minutes-down fa fas fa-chevron-down'
22332                                 }
22333                             ]
22334                         }
22335                     ]
22336                 },
22337                 {
22338                     tag: 'td',
22339                     cls: 'separator'
22340                 }
22341             ]
22342         });
22343         
22344     },
22345     
22346     update: function()
22347     {
22348         
22349         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22350         
22351         this.fill();
22352     },
22353     
22354     fill: function() 
22355     {
22356         var hours = this.time.getHours();
22357         var minutes = this.time.getMinutes();
22358         var period = 'AM';
22359         
22360         if(hours > 11){
22361             period = 'PM';
22362         }
22363         
22364         if(hours == 0){
22365             hours = 12;
22366         }
22367         
22368         
22369         if(hours > 12){
22370             hours = hours - 12;
22371         }
22372         
22373         if(hours < 10){
22374             hours = '0' + hours;
22375         }
22376         
22377         if(minutes < 10){
22378             minutes = '0' + minutes;
22379         }
22380         
22381         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22382         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22383         this.pop.select('button', true).first().dom.innerHTML = period;
22384         
22385     },
22386     
22387     place: function()
22388     {   
22389         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22390         
22391         var cls = ['bottom'];
22392         
22393         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22394             cls.pop();
22395             cls.push('top');
22396         }
22397         
22398         cls.push('right');
22399         
22400         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22401             cls.pop();
22402             cls.push('left');
22403         }
22404         //this.picker().setXY(20000,20000);
22405         this.picker().addClass(cls.join('-'));
22406         
22407         var _this = this;
22408         
22409         Roo.each(cls, function(c){
22410             if(c == 'bottom'){
22411                 (function() {
22412                  //  
22413                 }).defer(200);
22414                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22415                 //_this.picker().setTop(_this.inputEl().getHeight());
22416                 return;
22417             }
22418             if(c == 'top'){
22419                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22420                 
22421                 //_this.picker().setTop(0 - _this.picker().getHeight());
22422                 return;
22423             }
22424             /*
22425             if(c == 'left'){
22426                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22427                 return;
22428             }
22429             if(c == 'right'){
22430                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22431                 return;
22432             }
22433             */
22434         });
22435         
22436     },
22437   
22438     onFocus : function()
22439     {
22440         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22441         this.show();
22442     },
22443     
22444     onBlur : function()
22445     {
22446         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22447         this.hide();
22448     },
22449     
22450     show : function()
22451     {
22452         this.picker().show();
22453         this.pop.show();
22454         this.update();
22455         this.place();
22456         
22457         this.fireEvent('show', this, this.date);
22458     },
22459     
22460     hide : function()
22461     {
22462         this.picker().hide();
22463         this.pop.hide();
22464         
22465         this.fireEvent('hide', this, this.date);
22466     },
22467     
22468     setTime : function()
22469     {
22470         this.hide();
22471         this.setValue(this.time.format(this.format));
22472         
22473         this.fireEvent('select', this, this.date);
22474         
22475         
22476     },
22477     
22478     onMousedown: function(e){
22479         e.stopPropagation();
22480         e.preventDefault();
22481     },
22482     
22483     onIncrementHours: function()
22484     {
22485         Roo.log('onIncrementHours');
22486         this.time = this.time.add(Date.HOUR, 1);
22487         this.update();
22488         
22489     },
22490     
22491     onDecrementHours: function()
22492     {
22493         Roo.log('onDecrementHours');
22494         this.time = this.time.add(Date.HOUR, -1);
22495         this.update();
22496     },
22497     
22498     onIncrementMinutes: function()
22499     {
22500         Roo.log('onIncrementMinutes');
22501         this.time = this.time.add(Date.MINUTE, 1);
22502         this.update();
22503     },
22504     
22505     onDecrementMinutes: function()
22506     {
22507         Roo.log('onDecrementMinutes');
22508         this.time = this.time.add(Date.MINUTE, -1);
22509         this.update();
22510     },
22511     
22512     onTogglePeriod: function()
22513     {
22514         Roo.log('onTogglePeriod');
22515         this.time = this.time.add(Date.HOUR, 12);
22516         this.update();
22517     }
22518     
22519    
22520 });
22521  
22522
22523 Roo.apply(Roo.bootstrap.TimeField,  {
22524   
22525     template : {
22526         tag: 'div',
22527         cls: 'datepicker dropdown-menu',
22528         cn: [
22529             {
22530                 tag: 'div',
22531                 cls: 'datepicker-time',
22532                 cn: [
22533                 {
22534                     tag: 'table',
22535                     cls: 'table-condensed',
22536                     cn:[
22537                         {
22538                             tag: 'tbody',
22539                             cn: [
22540                                 {
22541                                     tag: 'tr',
22542                                     cn: [
22543                                     {
22544                                         tag: 'td',
22545                                         colspan: '7'
22546                                     }
22547                                     ]
22548                                 }
22549                             ]
22550                         },
22551                         {
22552                             tag: 'tfoot',
22553                             cn: [
22554                                 {
22555                                     tag: 'tr',
22556                                     cn: [
22557                                     {
22558                                         tag: 'th',
22559                                         colspan: '7',
22560                                         cls: '',
22561                                         cn: [
22562                                             {
22563                                                 tag: 'button',
22564                                                 cls: 'btn btn-info ok',
22565                                                 html: 'OK'
22566                                             }
22567                                         ]
22568                                     }
22569                     
22570                                     ]
22571                                 }
22572                             ]
22573                         }
22574                     ]
22575                 }
22576                 ]
22577             }
22578         ]
22579     }
22580 });
22581
22582  
22583
22584  /*
22585  * - LGPL
22586  *
22587  * MonthField
22588  * 
22589  */
22590
22591 /**
22592  * @class Roo.bootstrap.MonthField
22593  * @extends Roo.bootstrap.Input
22594  * Bootstrap MonthField class
22595  * 
22596  * @cfg {String} language default en
22597  * 
22598  * @constructor
22599  * Create a new MonthField
22600  * @param {Object} config The config object
22601  */
22602
22603 Roo.bootstrap.MonthField = function(config){
22604     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22605     
22606     this.addEvents({
22607         /**
22608          * @event show
22609          * Fires when this field show.
22610          * @param {Roo.bootstrap.MonthField} this
22611          * @param {Mixed} date The date value
22612          */
22613         show : true,
22614         /**
22615          * @event show
22616          * Fires when this field hide.
22617          * @param {Roo.bootstrap.MonthField} this
22618          * @param {Mixed} date The date value
22619          */
22620         hide : true,
22621         /**
22622          * @event select
22623          * Fires when select a date.
22624          * @param {Roo.bootstrap.MonthField} this
22625          * @param {String} oldvalue The old value
22626          * @param {String} newvalue The new value
22627          */
22628         select : true
22629     });
22630 };
22631
22632 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22633     
22634     onRender: function(ct, position)
22635     {
22636         
22637         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22638         
22639         this.language = this.language || 'en';
22640         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22641         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22642         
22643         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22644         this.isInline = false;
22645         this.isInput = true;
22646         this.component = this.el.select('.add-on', true).first() || false;
22647         this.component = (this.component && this.component.length === 0) ? false : this.component;
22648         this.hasInput = this.component && this.inputEL().length;
22649         
22650         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22651         
22652         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22653         
22654         this.picker().on('mousedown', this.onMousedown, this);
22655         this.picker().on('click', this.onClick, this);
22656         
22657         this.picker().addClass('datepicker-dropdown');
22658         
22659         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22660             v.setStyle('width', '189px');
22661         });
22662         
22663         this.fillMonths();
22664         
22665         this.update();
22666         
22667         if(this.isInline) {
22668             this.show();
22669         }
22670         
22671     },
22672     
22673     setValue: function(v, suppressEvent)
22674     {   
22675         var o = this.getValue();
22676         
22677         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22678         
22679         this.update();
22680
22681         if(suppressEvent !== true){
22682             this.fireEvent('select', this, o, v);
22683         }
22684         
22685     },
22686     
22687     getValue: function()
22688     {
22689         return this.value;
22690     },
22691     
22692     onClick: function(e) 
22693     {
22694         e.stopPropagation();
22695         e.preventDefault();
22696         
22697         var target = e.getTarget();
22698         
22699         if(target.nodeName.toLowerCase() === 'i'){
22700             target = Roo.get(target).dom.parentNode;
22701         }
22702         
22703         var nodeName = target.nodeName;
22704         var className = target.className;
22705         var html = target.innerHTML;
22706         
22707         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22708             return;
22709         }
22710         
22711         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22712         
22713         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22714         
22715         this.hide();
22716                         
22717     },
22718     
22719     picker : function()
22720     {
22721         return this.pickerEl;
22722     },
22723     
22724     fillMonths: function()
22725     {    
22726         var i = 0;
22727         var months = this.picker().select('>.datepicker-months td', true).first();
22728         
22729         months.dom.innerHTML = '';
22730         
22731         while (i < 12) {
22732             var month = {
22733                 tag: 'span',
22734                 cls: 'month',
22735                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22736             };
22737             
22738             months.createChild(month);
22739         }
22740         
22741     },
22742     
22743     update: function()
22744     {
22745         var _this = this;
22746         
22747         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22748             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22749         }
22750         
22751         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22752             e.removeClass('active');
22753             
22754             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22755                 e.addClass('active');
22756             }
22757         })
22758     },
22759     
22760     place: function()
22761     {
22762         if(this.isInline) {
22763             return;
22764         }
22765         
22766         this.picker().removeClass(['bottom', 'top']);
22767         
22768         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22769             /*
22770              * place to the top of element!
22771              *
22772              */
22773             
22774             this.picker().addClass('top');
22775             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22776             
22777             return;
22778         }
22779         
22780         this.picker().addClass('bottom');
22781         
22782         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22783     },
22784     
22785     onFocus : function()
22786     {
22787         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22788         this.show();
22789     },
22790     
22791     onBlur : function()
22792     {
22793         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22794         
22795         var d = this.inputEl().getValue();
22796         
22797         this.setValue(d);
22798                 
22799         this.hide();
22800     },
22801     
22802     show : function()
22803     {
22804         this.picker().show();
22805         this.picker().select('>.datepicker-months', true).first().show();
22806         this.update();
22807         this.place();
22808         
22809         this.fireEvent('show', this, this.date);
22810     },
22811     
22812     hide : function()
22813     {
22814         if(this.isInline) {
22815             return;
22816         }
22817         this.picker().hide();
22818         this.fireEvent('hide', this, this.date);
22819         
22820     },
22821     
22822     onMousedown: function(e)
22823     {
22824         e.stopPropagation();
22825         e.preventDefault();
22826     },
22827     
22828     keyup: function(e)
22829     {
22830         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22831         this.update();
22832     },
22833
22834     fireKey: function(e)
22835     {
22836         if (!this.picker().isVisible()){
22837             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22838                 this.show();
22839             }
22840             return;
22841         }
22842         
22843         var dir;
22844         
22845         switch(e.keyCode){
22846             case 27: // escape
22847                 this.hide();
22848                 e.preventDefault();
22849                 break;
22850             case 37: // left
22851             case 39: // right
22852                 dir = e.keyCode == 37 ? -1 : 1;
22853                 
22854                 this.vIndex = this.vIndex + dir;
22855                 
22856                 if(this.vIndex < 0){
22857                     this.vIndex = 0;
22858                 }
22859                 
22860                 if(this.vIndex > 11){
22861                     this.vIndex = 11;
22862                 }
22863                 
22864                 if(isNaN(this.vIndex)){
22865                     this.vIndex = 0;
22866                 }
22867                 
22868                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22869                 
22870                 break;
22871             case 38: // up
22872             case 40: // down
22873                 
22874                 dir = e.keyCode == 38 ? -1 : 1;
22875                 
22876                 this.vIndex = this.vIndex + dir * 4;
22877                 
22878                 if(this.vIndex < 0){
22879                     this.vIndex = 0;
22880                 }
22881                 
22882                 if(this.vIndex > 11){
22883                     this.vIndex = 11;
22884                 }
22885                 
22886                 if(isNaN(this.vIndex)){
22887                     this.vIndex = 0;
22888                 }
22889                 
22890                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22891                 break;
22892                 
22893             case 13: // enter
22894                 
22895                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22896                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22897                 }
22898                 
22899                 this.hide();
22900                 e.preventDefault();
22901                 break;
22902             case 9: // tab
22903                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22904                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22905                 }
22906                 this.hide();
22907                 break;
22908             case 16: // shift
22909             case 17: // ctrl
22910             case 18: // alt
22911                 break;
22912             default :
22913                 this.hide();
22914                 
22915         }
22916     },
22917     
22918     remove: function() 
22919     {
22920         this.picker().remove();
22921     }
22922    
22923 });
22924
22925 Roo.apply(Roo.bootstrap.MonthField,  {
22926     
22927     content : {
22928         tag: 'tbody',
22929         cn: [
22930         {
22931             tag: 'tr',
22932             cn: [
22933             {
22934                 tag: 'td',
22935                 colspan: '7'
22936             }
22937             ]
22938         }
22939         ]
22940     },
22941     
22942     dates:{
22943         en: {
22944             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22945             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22946         }
22947     }
22948 });
22949
22950 Roo.apply(Roo.bootstrap.MonthField,  {
22951   
22952     template : {
22953         tag: 'div',
22954         cls: 'datepicker dropdown-menu roo-dynamic',
22955         cn: [
22956             {
22957                 tag: 'div',
22958                 cls: 'datepicker-months',
22959                 cn: [
22960                 {
22961                     tag: 'table',
22962                     cls: 'table-condensed',
22963                     cn:[
22964                         Roo.bootstrap.DateField.content
22965                     ]
22966                 }
22967                 ]
22968             }
22969         ]
22970     }
22971 });
22972
22973  
22974
22975  
22976  /*
22977  * - LGPL
22978  *
22979  * CheckBox
22980  * 
22981  */
22982
22983 /**
22984  * @class Roo.bootstrap.CheckBox
22985  * @extends Roo.bootstrap.Input
22986  * Bootstrap CheckBox class
22987  * 
22988  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22989  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22990  * @cfg {String} boxLabel The text that appears beside the checkbox
22991  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22992  * @cfg {Boolean} checked initnal the element
22993  * @cfg {Boolean} inline inline the element (default false)
22994  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22995  * @cfg {String} tooltip label tooltip
22996  * 
22997  * @constructor
22998  * Create a new CheckBox
22999  * @param {Object} config The config object
23000  */
23001
23002 Roo.bootstrap.CheckBox = function(config){
23003     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
23004    
23005     this.addEvents({
23006         /**
23007         * @event check
23008         * Fires when the element is checked or unchecked.
23009         * @param {Roo.bootstrap.CheckBox} this This input
23010         * @param {Boolean} checked The new checked value
23011         */
23012        check : true,
23013        /**
23014         * @event click
23015         * Fires when the element is click.
23016         * @param {Roo.bootstrap.CheckBox} this This input
23017         */
23018        click : true
23019     });
23020     
23021 };
23022
23023 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
23024   
23025     inputType: 'checkbox',
23026     inputValue: 1,
23027     valueOff: 0,
23028     boxLabel: false,
23029     checked: false,
23030     weight : false,
23031     inline: false,
23032     tooltip : '',
23033     
23034     // checkbox success does not make any sense really.. 
23035     invalidClass : "",
23036     validClass : "",
23037     
23038     
23039     getAutoCreate : function()
23040     {
23041         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
23042         
23043         var id = Roo.id();
23044         
23045         var cfg = {};
23046         
23047         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
23048         
23049         if(this.inline){
23050             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23051         }
23052         
23053         var input =  {
23054             tag: 'input',
23055             id : id,
23056             type : this.inputType,
23057             value : this.inputValue,
23058             cls : 'roo-' + this.inputType, //'form-box',
23059             placeholder : this.placeholder || ''
23060             
23061         };
23062         
23063         if(this.inputType != 'radio'){
23064             var hidden =  {
23065                 tag: 'input',
23066                 type : 'hidden',
23067                 cls : 'roo-hidden-value',
23068                 value : this.checked ? this.inputValue : this.valueOff
23069             };
23070         }
23071         
23072             
23073         if (this.weight) { // Validity check?
23074             cfg.cls += " " + this.inputType + "-" + this.weight;
23075         }
23076         
23077         if (this.disabled) {
23078             input.disabled=true;
23079         }
23080         
23081         if(this.checked){
23082             input.checked = this.checked;
23083         }
23084         
23085         if (this.name) {
23086             
23087             input.name = this.name;
23088             
23089             if(this.inputType != 'radio'){
23090                 hidden.name = this.name;
23091                 input.name = '_hidden_' + this.name;
23092             }
23093         }
23094         
23095         if (this.size) {
23096             input.cls += ' input-' + this.size;
23097         }
23098         
23099         var settings=this;
23100         
23101         ['xs','sm','md','lg'].map(function(size){
23102             if (settings[size]) {
23103                 cfg.cls += ' col-' + size + '-' + settings[size];
23104             }
23105         });
23106         
23107         var inputblock = input;
23108          
23109         if (this.before || this.after) {
23110             
23111             inputblock = {
23112                 cls : 'input-group',
23113                 cn :  [] 
23114             };
23115             
23116             if (this.before) {
23117                 inputblock.cn.push({
23118                     tag :'span',
23119                     cls : 'input-group-addon',
23120                     html : this.before
23121                 });
23122             }
23123             
23124             inputblock.cn.push(input);
23125             
23126             if(this.inputType != 'radio'){
23127                 inputblock.cn.push(hidden);
23128             }
23129             
23130             if (this.after) {
23131                 inputblock.cn.push({
23132                     tag :'span',
23133                     cls : 'input-group-addon',
23134                     html : this.after
23135                 });
23136             }
23137             
23138         }
23139         var boxLabelCfg = false;
23140         
23141         if(this.boxLabel){
23142            
23143             boxLabelCfg = {
23144                 tag: 'label',
23145                 //'for': id, // box label is handled by onclick - so no for...
23146                 cls: 'box-label',
23147                 html: this.boxLabel
23148             };
23149             if(this.tooltip){
23150                 boxLabelCfg.tooltip = this.tooltip;
23151             }
23152              
23153         }
23154         
23155         
23156         if (align ==='left' && this.fieldLabel.length) {
23157 //                Roo.log("left and has label");
23158             cfg.cn = [
23159                 {
23160                     tag: 'label',
23161                     'for' :  id,
23162                     cls : 'control-label',
23163                     html : this.fieldLabel
23164                 },
23165                 {
23166                     cls : "", 
23167                     cn: [
23168                         inputblock
23169                     ]
23170                 }
23171             ];
23172             
23173             if (boxLabelCfg) {
23174                 cfg.cn[1].cn.push(boxLabelCfg);
23175             }
23176             
23177             if(this.labelWidth > 12){
23178                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23179             }
23180             
23181             if(this.labelWidth < 13 && this.labelmd == 0){
23182                 this.labelmd = this.labelWidth;
23183             }
23184             
23185             if(this.labellg > 0){
23186                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23187                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23188             }
23189             
23190             if(this.labelmd > 0){
23191                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23192                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23193             }
23194             
23195             if(this.labelsm > 0){
23196                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23197                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23198             }
23199             
23200             if(this.labelxs > 0){
23201                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23202                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23203             }
23204             
23205         } else if ( this.fieldLabel.length) {
23206 //                Roo.log(" label");
23207                 cfg.cn = [
23208                    
23209                     {
23210                         tag: this.boxLabel ? 'span' : 'label',
23211                         'for': id,
23212                         cls: 'control-label box-input-label',
23213                         //cls : 'input-group-addon',
23214                         html : this.fieldLabel
23215                     },
23216                     
23217                     inputblock
23218                     
23219                 ];
23220                 if (boxLabelCfg) {
23221                     cfg.cn.push(boxLabelCfg);
23222                 }
23223
23224         } else {
23225             
23226 //                Roo.log(" no label && no align");
23227                 cfg.cn = [  inputblock ] ;
23228                 if (boxLabelCfg) {
23229                     cfg.cn.push(boxLabelCfg);
23230                 }
23231
23232                 
23233         }
23234         
23235        
23236         
23237         if(this.inputType != 'radio'){
23238             cfg.cn.push(hidden);
23239         }
23240         
23241         return cfg;
23242         
23243     },
23244     
23245     /**
23246      * return the real input element.
23247      */
23248     inputEl: function ()
23249     {
23250         return this.el.select('input.roo-' + this.inputType,true).first();
23251     },
23252     hiddenEl: function ()
23253     {
23254         return this.el.select('input.roo-hidden-value',true).first();
23255     },
23256     
23257     labelEl: function()
23258     {
23259         return this.el.select('label.control-label',true).first();
23260     },
23261     /* depricated... */
23262     
23263     label: function()
23264     {
23265         return this.labelEl();
23266     },
23267     
23268     boxLabelEl: function()
23269     {
23270         return this.el.select('label.box-label',true).first();
23271     },
23272     
23273     initEvents : function()
23274     {
23275 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23276         
23277         this.inputEl().on('click', this.onClick,  this);
23278         
23279         if (this.boxLabel) { 
23280             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23281         }
23282         
23283         this.startValue = this.getValue();
23284         
23285         if(this.groupId){
23286             Roo.bootstrap.CheckBox.register(this);
23287         }
23288     },
23289     
23290     onClick : function(e)
23291     {   
23292         if(this.fireEvent('click', this, e) !== false){
23293             this.setChecked(!this.checked);
23294         }
23295         
23296     },
23297     
23298     setChecked : function(state,suppressEvent)
23299     {
23300         this.startValue = this.getValue();
23301
23302         if(this.inputType == 'radio'){
23303             
23304             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23305                 e.dom.checked = false;
23306             });
23307             
23308             this.inputEl().dom.checked = true;
23309             
23310             this.inputEl().dom.value = this.inputValue;
23311             
23312             if(suppressEvent !== true){
23313                 this.fireEvent('check', this, true);
23314             }
23315             
23316             this.validate();
23317             
23318             return;
23319         }
23320         
23321         this.checked = state;
23322         
23323         this.inputEl().dom.checked = state;
23324         
23325         
23326         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23327         
23328         if(suppressEvent !== true){
23329             this.fireEvent('check', this, state);
23330         }
23331         
23332         this.validate();
23333     },
23334     
23335     getValue : function()
23336     {
23337         if(this.inputType == 'radio'){
23338             return this.getGroupValue();
23339         }
23340         
23341         return this.hiddenEl().dom.value;
23342         
23343     },
23344     
23345     getGroupValue : function()
23346     {
23347         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23348             return '';
23349         }
23350         
23351         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23352     },
23353     
23354     setValue : function(v,suppressEvent)
23355     {
23356         if(this.inputType == 'radio'){
23357             this.setGroupValue(v, suppressEvent);
23358             return;
23359         }
23360         
23361         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23362         
23363         this.validate();
23364     },
23365     
23366     setGroupValue : function(v, suppressEvent)
23367     {
23368         this.startValue = this.getValue();
23369         
23370         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23371             e.dom.checked = false;
23372             
23373             if(e.dom.value == v){
23374                 e.dom.checked = true;
23375             }
23376         });
23377         
23378         if(suppressEvent !== true){
23379             this.fireEvent('check', this, true);
23380         }
23381
23382         this.validate();
23383         
23384         return;
23385     },
23386     
23387     validate : function()
23388     {
23389         if(this.getVisibilityEl().hasClass('hidden')){
23390             return true;
23391         }
23392         
23393         if(
23394                 this.disabled || 
23395                 (this.inputType == 'radio' && this.validateRadio()) ||
23396                 (this.inputType == 'checkbox' && this.validateCheckbox())
23397         ){
23398             this.markValid();
23399             return true;
23400         }
23401         
23402         this.markInvalid();
23403         return false;
23404     },
23405     
23406     validateRadio : function()
23407     {
23408         if(this.getVisibilityEl().hasClass('hidden')){
23409             return true;
23410         }
23411         
23412         if(this.allowBlank){
23413             return true;
23414         }
23415         
23416         var valid = false;
23417         
23418         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23419             if(!e.dom.checked){
23420                 return;
23421             }
23422             
23423             valid = true;
23424             
23425             return false;
23426         });
23427         
23428         return valid;
23429     },
23430     
23431     validateCheckbox : function()
23432     {
23433         if(!this.groupId){
23434             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23435             //return (this.getValue() == this.inputValue) ? true : false;
23436         }
23437         
23438         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23439         
23440         if(!group){
23441             return false;
23442         }
23443         
23444         var r = false;
23445         
23446         for(var i in group){
23447             if(group[i].el.isVisible(true)){
23448                 r = false;
23449                 break;
23450             }
23451             
23452             r = true;
23453         }
23454         
23455         for(var i in group){
23456             if(r){
23457                 break;
23458             }
23459             
23460             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23461         }
23462         
23463         return r;
23464     },
23465     
23466     /**
23467      * Mark this field as valid
23468      */
23469     markValid : function()
23470     {
23471         var _this = this;
23472         
23473         this.fireEvent('valid', this);
23474         
23475         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23476         
23477         if(this.groupId){
23478             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23479         }
23480         
23481         if(label){
23482             label.markValid();
23483         }
23484
23485         if(this.inputType == 'radio'){
23486             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23487                 var fg = e.findParent('.form-group', false, true);
23488                 if (Roo.bootstrap.version == 3) {
23489                     fg.removeClass([_this.invalidClass, _this.validClass]);
23490                     fg.addClass(_this.validClass);
23491                 } else {
23492                     fg.removeClass(['is-valid', 'is-invalid']);
23493                     fg.addClass('is-valid');
23494                 }
23495             });
23496             
23497             return;
23498         }
23499
23500         if(!this.groupId){
23501             var fg = this.el.findParent('.form-group', false, true);
23502             if (Roo.bootstrap.version == 3) {
23503                 fg.removeClass([this.invalidClass, this.validClass]);
23504                 fg.addClass(this.validClass);
23505             } else {
23506                 fg.removeClass(['is-valid', 'is-invalid']);
23507                 fg.addClass('is-valid');
23508             }
23509             return;
23510         }
23511         
23512         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23513         
23514         if(!group){
23515             return;
23516         }
23517         
23518         for(var i in group){
23519             var fg = group[i].el.findParent('.form-group', false, true);
23520             if (Roo.bootstrap.version == 3) {
23521                 fg.removeClass([this.invalidClass, this.validClass]);
23522                 fg.addClass(this.validClass);
23523             } else {
23524                 fg.removeClass(['is-valid', 'is-invalid']);
23525                 fg.addClass('is-valid');
23526             }
23527         }
23528     },
23529     
23530      /**
23531      * Mark this field as invalid
23532      * @param {String} msg The validation message
23533      */
23534     markInvalid : function(msg)
23535     {
23536         if(this.allowBlank){
23537             return;
23538         }
23539         
23540         var _this = this;
23541         
23542         this.fireEvent('invalid', this, msg);
23543         
23544         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23545         
23546         if(this.groupId){
23547             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23548         }
23549         
23550         if(label){
23551             label.markInvalid();
23552         }
23553             
23554         if(this.inputType == 'radio'){
23555             
23556             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23557                 var fg = e.findParent('.form-group', false, true);
23558                 if (Roo.bootstrap.version == 3) {
23559                     fg.removeClass([_this.invalidClass, _this.validClass]);
23560                     fg.addClass(_this.invalidClass);
23561                 } else {
23562                     fg.removeClass(['is-invalid', 'is-valid']);
23563                     fg.addClass('is-invalid');
23564                 }
23565             });
23566             
23567             return;
23568         }
23569         
23570         if(!this.groupId){
23571             var fg = this.el.findParent('.form-group', false, true);
23572             if (Roo.bootstrap.version == 3) {
23573                 fg.removeClass([_this.invalidClass, _this.validClass]);
23574                 fg.addClass(_this.invalidClass);
23575             } else {
23576                 fg.removeClass(['is-invalid', 'is-valid']);
23577                 fg.addClass('is-invalid');
23578             }
23579             return;
23580         }
23581         
23582         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23583         
23584         if(!group){
23585             return;
23586         }
23587         
23588         for(var i in group){
23589             var fg = group[i].el.findParent('.form-group', false, true);
23590             if (Roo.bootstrap.version == 3) {
23591                 fg.removeClass([_this.invalidClass, _this.validClass]);
23592                 fg.addClass(_this.invalidClass);
23593             } else {
23594                 fg.removeClass(['is-invalid', 'is-valid']);
23595                 fg.addClass('is-invalid');
23596             }
23597         }
23598         
23599     },
23600     
23601     clearInvalid : function()
23602     {
23603         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23604         
23605         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23606         
23607         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23608         
23609         if (label && label.iconEl) {
23610             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23611             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23612         }
23613     },
23614     
23615     disable : function()
23616     {
23617         if(this.inputType != 'radio'){
23618             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23619             return;
23620         }
23621         
23622         var _this = this;
23623         
23624         if(this.rendered){
23625             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23626                 _this.getActionEl().addClass(this.disabledClass);
23627                 e.dom.disabled = true;
23628             });
23629         }
23630         
23631         this.disabled = true;
23632         this.fireEvent("disable", this);
23633         return this;
23634     },
23635
23636     enable : function()
23637     {
23638         if(this.inputType != 'radio'){
23639             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23640             return;
23641         }
23642         
23643         var _this = this;
23644         
23645         if(this.rendered){
23646             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23647                 _this.getActionEl().removeClass(this.disabledClass);
23648                 e.dom.disabled = false;
23649             });
23650         }
23651         
23652         this.disabled = false;
23653         this.fireEvent("enable", this);
23654         return this;
23655     },
23656     
23657     setBoxLabel : function(v)
23658     {
23659         this.boxLabel = v;
23660         
23661         if(this.rendered){
23662             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23663         }
23664     }
23665
23666 });
23667
23668 Roo.apply(Roo.bootstrap.CheckBox, {
23669     
23670     groups: {},
23671     
23672      /**
23673     * register a CheckBox Group
23674     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23675     */
23676     register : function(checkbox)
23677     {
23678         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23679             this.groups[checkbox.groupId] = {};
23680         }
23681         
23682         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23683             return;
23684         }
23685         
23686         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23687         
23688     },
23689     /**
23690     * fetch a CheckBox Group based on the group ID
23691     * @param {string} the group ID
23692     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23693     */
23694     get: function(groupId) {
23695         if (typeof(this.groups[groupId]) == 'undefined') {
23696             return false;
23697         }
23698         
23699         return this.groups[groupId] ;
23700     }
23701     
23702     
23703 });
23704 /*
23705  * - LGPL
23706  *
23707  * RadioItem
23708  * 
23709  */
23710
23711 /**
23712  * @class Roo.bootstrap.Radio
23713  * @extends Roo.bootstrap.Component
23714  * Bootstrap Radio class
23715  * @cfg {String} boxLabel - the label associated
23716  * @cfg {String} value - the value of radio
23717  * 
23718  * @constructor
23719  * Create a new Radio
23720  * @param {Object} config The config object
23721  */
23722 Roo.bootstrap.Radio = function(config){
23723     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23724     
23725 };
23726
23727 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23728     
23729     boxLabel : '',
23730     
23731     value : '',
23732     
23733     getAutoCreate : function()
23734     {
23735         var cfg = {
23736             tag : 'div',
23737             cls : 'form-group radio',
23738             cn : [
23739                 {
23740                     tag : 'label',
23741                     cls : 'box-label',
23742                     html : this.boxLabel
23743                 }
23744             ]
23745         };
23746         
23747         return cfg;
23748     },
23749     
23750     initEvents : function() 
23751     {
23752         this.parent().register(this);
23753         
23754         this.el.on('click', this.onClick, this);
23755         
23756     },
23757     
23758     onClick : function(e)
23759     {
23760         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23761             this.setChecked(true);
23762         }
23763     },
23764     
23765     setChecked : function(state, suppressEvent)
23766     {
23767         this.parent().setValue(this.value, suppressEvent);
23768         
23769     },
23770     
23771     setBoxLabel : function(v)
23772     {
23773         this.boxLabel = v;
23774         
23775         if(this.rendered){
23776             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23777         }
23778     }
23779     
23780 });
23781  
23782
23783  /*
23784  * - LGPL
23785  *
23786  * Input
23787  * 
23788  */
23789
23790 /**
23791  * @class Roo.bootstrap.SecurePass
23792  * @extends Roo.bootstrap.Input
23793  * Bootstrap SecurePass class
23794  *
23795  * 
23796  * @constructor
23797  * Create a new SecurePass
23798  * @param {Object} config The config object
23799  */
23800  
23801 Roo.bootstrap.SecurePass = function (config) {
23802     // these go here, so the translation tool can replace them..
23803     this.errors = {
23804         PwdEmpty: "Please type a password, and then retype it to confirm.",
23805         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23806         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23807         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23808         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23809         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23810         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23811         TooWeak: "Your password is Too Weak."
23812     },
23813     this.meterLabel = "Password strength:";
23814     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23815     this.meterClass = [
23816         "roo-password-meter-tooweak", 
23817         "roo-password-meter-weak", 
23818         "roo-password-meter-medium", 
23819         "roo-password-meter-strong", 
23820         "roo-password-meter-grey"
23821     ];
23822     
23823     this.errors = {};
23824     
23825     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23826 }
23827
23828 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23829     /**
23830      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23831      * {
23832      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23833      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23834      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23835      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23836      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23837      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23838      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23839      * })
23840      */
23841     // private
23842     
23843     meterWidth: 300,
23844     errorMsg :'',    
23845     errors: false,
23846     imageRoot: '/',
23847     /**
23848      * @cfg {String/Object} Label for the strength meter (defaults to
23849      * 'Password strength:')
23850      */
23851     // private
23852     meterLabel: '',
23853     /**
23854      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23855      * ['Weak', 'Medium', 'Strong'])
23856      */
23857     // private    
23858     pwdStrengths: false,    
23859     // private
23860     strength: 0,
23861     // private
23862     _lastPwd: null,
23863     // private
23864     kCapitalLetter: 0,
23865     kSmallLetter: 1,
23866     kDigit: 2,
23867     kPunctuation: 3,
23868     
23869     insecure: false,
23870     // private
23871     initEvents: function ()
23872     {
23873         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23874
23875         if (this.el.is('input[type=password]') && Roo.isSafari) {
23876             this.el.on('keydown', this.SafariOnKeyDown, this);
23877         }
23878
23879         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23880     },
23881     // private
23882     onRender: function (ct, position)
23883     {
23884         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23885         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23886         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23887
23888         this.trigger.createChild({
23889                    cn: [
23890                     {
23891                     //id: 'PwdMeter',
23892                     tag: 'div',
23893                     cls: 'roo-password-meter-grey col-xs-12',
23894                     style: {
23895                         //width: 0,
23896                         //width: this.meterWidth + 'px'                                                
23897                         }
23898                     },
23899                     {                            
23900                          cls: 'roo-password-meter-text'                          
23901                     }
23902                 ]            
23903         });
23904
23905          
23906         if (this.hideTrigger) {
23907             this.trigger.setDisplayed(false);
23908         }
23909         this.setSize(this.width || '', this.height || '');
23910     },
23911     // private
23912     onDestroy: function ()
23913     {
23914         if (this.trigger) {
23915             this.trigger.removeAllListeners();
23916             this.trigger.remove();
23917         }
23918         if (this.wrap) {
23919             this.wrap.remove();
23920         }
23921         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23922     },
23923     // private
23924     checkStrength: function ()
23925     {
23926         var pwd = this.inputEl().getValue();
23927         if (pwd == this._lastPwd) {
23928             return;
23929         }
23930
23931         var strength;
23932         if (this.ClientSideStrongPassword(pwd)) {
23933             strength = 3;
23934         } else if (this.ClientSideMediumPassword(pwd)) {
23935             strength = 2;
23936         } else if (this.ClientSideWeakPassword(pwd)) {
23937             strength = 1;
23938         } else {
23939             strength = 0;
23940         }
23941         
23942         Roo.log('strength1: ' + strength);
23943         
23944         //var pm = this.trigger.child('div/div/div').dom;
23945         var pm = this.trigger.child('div/div');
23946         pm.removeClass(this.meterClass);
23947         pm.addClass(this.meterClass[strength]);
23948                 
23949         
23950         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23951                 
23952         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23953         
23954         this._lastPwd = pwd;
23955     },
23956     reset: function ()
23957     {
23958         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23959         
23960         this._lastPwd = '';
23961         
23962         var pm = this.trigger.child('div/div');
23963         pm.removeClass(this.meterClass);
23964         pm.addClass('roo-password-meter-grey');        
23965         
23966         
23967         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23968         
23969         pt.innerHTML = '';
23970         this.inputEl().dom.type='password';
23971     },
23972     // private
23973     validateValue: function (value)
23974     {
23975         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23976             return false;
23977         }
23978         if (value.length == 0) {
23979             if (this.allowBlank) {
23980                 this.clearInvalid();
23981                 return true;
23982             }
23983
23984             this.markInvalid(this.errors.PwdEmpty);
23985             this.errorMsg = this.errors.PwdEmpty;
23986             return false;
23987         }
23988         
23989         if(this.insecure){
23990             return true;
23991         }
23992         
23993         if (!value.match(/[\x21-\x7e]+/)) {
23994             this.markInvalid(this.errors.PwdBadChar);
23995             this.errorMsg = this.errors.PwdBadChar;
23996             return false;
23997         }
23998         if (value.length < 6) {
23999             this.markInvalid(this.errors.PwdShort);
24000             this.errorMsg = this.errors.PwdShort;
24001             return false;
24002         }
24003         if (value.length > 16) {
24004             this.markInvalid(this.errors.PwdLong);
24005             this.errorMsg = this.errors.PwdLong;
24006             return false;
24007         }
24008         var strength;
24009         if (this.ClientSideStrongPassword(value)) {
24010             strength = 3;
24011         } else if (this.ClientSideMediumPassword(value)) {
24012             strength = 2;
24013         } else if (this.ClientSideWeakPassword(value)) {
24014             strength = 1;
24015         } else {
24016             strength = 0;
24017         }
24018
24019         
24020         if (strength < 2) {
24021             //this.markInvalid(this.errors.TooWeak);
24022             this.errorMsg = this.errors.TooWeak;
24023             //return false;
24024         }
24025         
24026         
24027         console.log('strength2: ' + strength);
24028         
24029         //var pm = this.trigger.child('div/div/div').dom;
24030         
24031         var pm = this.trigger.child('div/div');
24032         pm.removeClass(this.meterClass);
24033         pm.addClass(this.meterClass[strength]);
24034                 
24035         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
24036                 
24037         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
24038         
24039         this.errorMsg = ''; 
24040         return true;
24041     },
24042     // private
24043     CharacterSetChecks: function (type)
24044     {
24045         this.type = type;
24046         this.fResult = false;
24047     },
24048     // private
24049     isctype: function (character, type)
24050     {
24051         switch (type) {  
24052             case this.kCapitalLetter:
24053                 if (character >= 'A' && character <= 'Z') {
24054                     return true;
24055                 }
24056                 break;
24057             
24058             case this.kSmallLetter:
24059                 if (character >= 'a' && character <= 'z') {
24060                     return true;
24061                 }
24062                 break;
24063             
24064             case this.kDigit:
24065                 if (character >= '0' && character <= '9') {
24066                     return true;
24067                 }
24068                 break;
24069             
24070             case this.kPunctuation:
24071                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24072                     return true;
24073                 }
24074                 break;
24075             
24076             default:
24077                 return false;
24078         }
24079
24080     },
24081     // private
24082     IsLongEnough: function (pwd, size)
24083     {
24084         return !(pwd == null || isNaN(size) || pwd.length < size);
24085     },
24086     // private
24087     SpansEnoughCharacterSets: function (word, nb)
24088     {
24089         if (!this.IsLongEnough(word, nb))
24090         {
24091             return false;
24092         }
24093
24094         var characterSetChecks = new Array(
24095             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24096             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24097         );
24098         
24099         for (var index = 0; index < word.length; ++index) {
24100             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24101                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24102                     characterSetChecks[nCharSet].fResult = true;
24103                     break;
24104                 }
24105             }
24106         }
24107
24108         var nCharSets = 0;
24109         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24110             if (characterSetChecks[nCharSet].fResult) {
24111                 ++nCharSets;
24112             }
24113         }
24114
24115         if (nCharSets < nb) {
24116             return false;
24117         }
24118         return true;
24119     },
24120     // private
24121     ClientSideStrongPassword: function (pwd)
24122     {
24123         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24124     },
24125     // private
24126     ClientSideMediumPassword: function (pwd)
24127     {
24128         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24129     },
24130     // private
24131     ClientSideWeakPassword: function (pwd)
24132     {
24133         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24134     }
24135           
24136 })//<script type="text/javascript">
24137
24138 /*
24139  * Based  Ext JS Library 1.1.1
24140  * Copyright(c) 2006-2007, Ext JS, LLC.
24141  * LGPL
24142  *
24143  */
24144  
24145 /**
24146  * @class Roo.HtmlEditorCore
24147  * @extends Roo.Component
24148  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24149  *
24150  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24151  */
24152
24153 Roo.HtmlEditorCore = function(config){
24154     
24155     
24156     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24157     
24158     
24159     this.addEvents({
24160         /**
24161          * @event initialize
24162          * Fires when the editor is fully initialized (including the iframe)
24163          * @param {Roo.HtmlEditorCore} this
24164          */
24165         initialize: true,
24166         /**
24167          * @event activate
24168          * Fires when the editor is first receives the focus. Any insertion must wait
24169          * until after this event.
24170          * @param {Roo.HtmlEditorCore} this
24171          */
24172         activate: true,
24173          /**
24174          * @event beforesync
24175          * Fires before the textarea is updated with content from the editor iframe. Return false
24176          * to cancel the sync.
24177          * @param {Roo.HtmlEditorCore} this
24178          * @param {String} html
24179          */
24180         beforesync: true,
24181          /**
24182          * @event beforepush
24183          * Fires before the iframe editor is updated with content from the textarea. Return false
24184          * to cancel the push.
24185          * @param {Roo.HtmlEditorCore} this
24186          * @param {String} html
24187          */
24188         beforepush: true,
24189          /**
24190          * @event sync
24191          * Fires when the textarea is updated with content from the editor iframe.
24192          * @param {Roo.HtmlEditorCore} this
24193          * @param {String} html
24194          */
24195         sync: true,
24196          /**
24197          * @event push
24198          * Fires when the iframe editor is updated with content from the textarea.
24199          * @param {Roo.HtmlEditorCore} this
24200          * @param {String} html
24201          */
24202         push: true,
24203         
24204         /**
24205          * @event editorevent
24206          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24207          * @param {Roo.HtmlEditorCore} this
24208          */
24209         editorevent: true
24210         
24211     });
24212     
24213     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24214     
24215     // defaults : white / black...
24216     this.applyBlacklists();
24217     
24218     
24219     
24220 };
24221
24222
24223 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24224
24225
24226      /**
24227      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24228      */
24229     
24230     owner : false,
24231     
24232      /**
24233      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24234      *                        Roo.resizable.
24235      */
24236     resizable : false,
24237      /**
24238      * @cfg {Number} height (in pixels)
24239      */   
24240     height: 300,
24241    /**
24242      * @cfg {Number} width (in pixels)
24243      */   
24244     width: 500,
24245     
24246     /**
24247      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24248      * 
24249      */
24250     stylesheets: false,
24251     
24252     // id of frame..
24253     frameId: false,
24254     
24255     // private properties
24256     validationEvent : false,
24257     deferHeight: true,
24258     initialized : false,
24259     activated : false,
24260     sourceEditMode : false,
24261     onFocus : Roo.emptyFn,
24262     iframePad:3,
24263     hideMode:'offsets',
24264     
24265     clearUp: true,
24266     
24267     // blacklist + whitelisted elements..
24268     black: false,
24269     white: false,
24270      
24271     bodyCls : '',
24272
24273     /**
24274      * Protected method that will not generally be called directly. It
24275      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24276      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24277      */
24278     getDocMarkup : function(){
24279         // body styles..
24280         var st = '';
24281         
24282         // inherit styels from page...?? 
24283         if (this.stylesheets === false) {
24284             
24285             Roo.get(document.head).select('style').each(function(node) {
24286                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24287             });
24288             
24289             Roo.get(document.head).select('link').each(function(node) { 
24290                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24291             });
24292             
24293         } else if (!this.stylesheets.length) {
24294                 // simple..
24295                 st = '<style type="text/css">' +
24296                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24297                    '</style>';
24298         } else {
24299             for (var i in this.stylesheets) { 
24300                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24301             }
24302             
24303         }
24304         
24305         st +=  '<style type="text/css">' +
24306             'IMG { cursor: pointer } ' +
24307         '</style>';
24308
24309         var cls = 'roo-htmleditor-body';
24310         
24311         if(this.bodyCls.length){
24312             cls += ' ' + this.bodyCls;
24313         }
24314         
24315         return '<html><head>' + st  +
24316             //<style type="text/css">' +
24317             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24318             //'</style>' +
24319             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24320     },
24321
24322     // private
24323     onRender : function(ct, position)
24324     {
24325         var _t = this;
24326         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24327         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24328         
24329         
24330         this.el.dom.style.border = '0 none';
24331         this.el.dom.setAttribute('tabIndex', -1);
24332         this.el.addClass('x-hidden hide');
24333         
24334         
24335         
24336         if(Roo.isIE){ // fix IE 1px bogus margin
24337             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24338         }
24339        
24340         
24341         this.frameId = Roo.id();
24342         
24343          
24344         
24345         var iframe = this.owner.wrap.createChild({
24346             tag: 'iframe',
24347             cls: 'form-control', // bootstrap..
24348             id: this.frameId,
24349             name: this.frameId,
24350             frameBorder : 'no',
24351             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24352         }, this.el
24353         );
24354         
24355         
24356         this.iframe = iframe.dom;
24357
24358          this.assignDocWin();
24359         
24360         this.doc.designMode = 'on';
24361        
24362         this.doc.open();
24363         this.doc.write(this.getDocMarkup());
24364         this.doc.close();
24365
24366         
24367         var task = { // must defer to wait for browser to be ready
24368             run : function(){
24369                 //console.log("run task?" + this.doc.readyState);
24370                 this.assignDocWin();
24371                 if(this.doc.body || this.doc.readyState == 'complete'){
24372                     try {
24373                         this.doc.designMode="on";
24374                     } catch (e) {
24375                         return;
24376                     }
24377                     Roo.TaskMgr.stop(task);
24378                     this.initEditor.defer(10, this);
24379                 }
24380             },
24381             interval : 10,
24382             duration: 10000,
24383             scope: this
24384         };
24385         Roo.TaskMgr.start(task);
24386
24387     },
24388
24389     // private
24390     onResize : function(w, h)
24391     {
24392          Roo.log('resize: ' +w + ',' + h );
24393         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24394         if(!this.iframe){
24395             return;
24396         }
24397         if(typeof w == 'number'){
24398             
24399             this.iframe.style.width = w + 'px';
24400         }
24401         if(typeof h == 'number'){
24402             
24403             this.iframe.style.height = h + 'px';
24404             if(this.doc){
24405                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24406             }
24407         }
24408         
24409     },
24410
24411     /**
24412      * Toggles the editor between standard and source edit mode.
24413      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24414      */
24415     toggleSourceEdit : function(sourceEditMode){
24416         
24417         this.sourceEditMode = sourceEditMode === true;
24418         
24419         if(this.sourceEditMode){
24420  
24421             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24422             
24423         }else{
24424             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24425             //this.iframe.className = '';
24426             this.deferFocus();
24427         }
24428         //this.setSize(this.owner.wrap.getSize());
24429         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24430     },
24431
24432     
24433   
24434
24435     /**
24436      * Protected method that will not generally be called directly. If you need/want
24437      * custom HTML cleanup, this is the method you should override.
24438      * @param {String} html The HTML to be cleaned
24439      * return {String} The cleaned HTML
24440      */
24441     cleanHtml : function(html){
24442         html = String(html);
24443         if(html.length > 5){
24444             if(Roo.isSafari){ // strip safari nonsense
24445                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24446             }
24447         }
24448         if(html == '&nbsp;'){
24449             html = '';
24450         }
24451         return html;
24452     },
24453
24454     /**
24455      * HTML Editor -> Textarea
24456      * Protected method that will not generally be called directly. Syncs the contents
24457      * of the editor iframe with the textarea.
24458      */
24459     syncValue : function(){
24460         if(this.initialized){
24461             var bd = (this.doc.body || this.doc.documentElement);
24462             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24463             var html = bd.innerHTML;
24464             if(Roo.isSafari){
24465                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24466                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24467                 if(m && m[1]){
24468                     html = '<div style="'+m[0]+'">' + html + '</div>';
24469                 }
24470             }
24471             html = this.cleanHtml(html);
24472             // fix up the special chars.. normaly like back quotes in word...
24473             // however we do not want to do this with chinese..
24474             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24475                 
24476                 var cc = match.charCodeAt();
24477
24478                 // Get the character value, handling surrogate pairs
24479                 if (match.length == 2) {
24480                     // It's a surrogate pair, calculate the Unicode code point
24481                     var high = match.charCodeAt(0) - 0xD800;
24482                     var low  = match.charCodeAt(1) - 0xDC00;
24483                     cc = (high * 0x400) + low + 0x10000;
24484                 }  else if (
24485                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24486                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24487                     (cc >= 0xf900 && cc < 0xfb00 )
24488                 ) {
24489                         return match;
24490                 }  
24491          
24492                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24493                 return "&#" + cc + ";";
24494                 
24495                 
24496             });
24497             
24498             
24499              
24500             if(this.owner.fireEvent('beforesync', this, html) !== false){
24501                 this.el.dom.value = html;
24502                 this.owner.fireEvent('sync', this, html);
24503             }
24504         }
24505     },
24506
24507     /**
24508      * Protected method that will not generally be called directly. Pushes the value of the textarea
24509      * into the iframe editor.
24510      */
24511     pushValue : function(){
24512         if(this.initialized){
24513             var v = this.el.dom.value.trim();
24514             
24515 //            if(v.length < 1){
24516 //                v = '&#160;';
24517 //            }
24518             
24519             if(this.owner.fireEvent('beforepush', this, v) !== false){
24520                 var d = (this.doc.body || this.doc.documentElement);
24521                 d.innerHTML = v;
24522                 this.cleanUpPaste();
24523                 this.el.dom.value = d.innerHTML;
24524                 this.owner.fireEvent('push', this, v);
24525             }
24526         }
24527     },
24528
24529     // private
24530     deferFocus : function(){
24531         this.focus.defer(10, this);
24532     },
24533
24534     // doc'ed in Field
24535     focus : function(){
24536         if(this.win && !this.sourceEditMode){
24537             this.win.focus();
24538         }else{
24539             this.el.focus();
24540         }
24541     },
24542     
24543     assignDocWin: function()
24544     {
24545         var iframe = this.iframe;
24546         
24547          if(Roo.isIE){
24548             this.doc = iframe.contentWindow.document;
24549             this.win = iframe.contentWindow;
24550         } else {
24551 //            if (!Roo.get(this.frameId)) {
24552 //                return;
24553 //            }
24554 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24555 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24556             
24557             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24558                 return;
24559             }
24560             
24561             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24562             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24563         }
24564     },
24565     
24566     // private
24567     initEditor : function(){
24568         //console.log("INIT EDITOR");
24569         this.assignDocWin();
24570         
24571         
24572         
24573         this.doc.designMode="on";
24574         this.doc.open();
24575         this.doc.write(this.getDocMarkup());
24576         this.doc.close();
24577         
24578         var dbody = (this.doc.body || this.doc.documentElement);
24579         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24580         // this copies styles from the containing element into thsi one..
24581         // not sure why we need all of this..
24582         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24583         
24584         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24585         //ss['background-attachment'] = 'fixed'; // w3c
24586         dbody.bgProperties = 'fixed'; // ie
24587         //Roo.DomHelper.applyStyles(dbody, ss);
24588         Roo.EventManager.on(this.doc, {
24589             //'mousedown': this.onEditorEvent,
24590             'mouseup': this.onEditorEvent,
24591             'dblclick': this.onEditorEvent,
24592             'click': this.onEditorEvent,
24593             'keyup': this.onEditorEvent,
24594             buffer:100,
24595             scope: this
24596         });
24597         if(Roo.isGecko){
24598             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24599         }
24600         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24601             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24602         }
24603         this.initialized = true;
24604
24605         this.owner.fireEvent('initialize', this);
24606         this.pushValue();
24607     },
24608
24609     // private
24610     onDestroy : function(){
24611         
24612         
24613         
24614         if(this.rendered){
24615             
24616             //for (var i =0; i < this.toolbars.length;i++) {
24617             //    // fixme - ask toolbars for heights?
24618             //    this.toolbars[i].onDestroy();
24619            // }
24620             
24621             //this.wrap.dom.innerHTML = '';
24622             //this.wrap.remove();
24623         }
24624     },
24625
24626     // private
24627     onFirstFocus : function(){
24628         
24629         this.assignDocWin();
24630         
24631         
24632         this.activated = true;
24633          
24634     
24635         if(Roo.isGecko){ // prevent silly gecko errors
24636             this.win.focus();
24637             var s = this.win.getSelection();
24638             if(!s.focusNode || s.focusNode.nodeType != 3){
24639                 var r = s.getRangeAt(0);
24640                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24641                 r.collapse(true);
24642                 this.deferFocus();
24643             }
24644             try{
24645                 this.execCmd('useCSS', true);
24646                 this.execCmd('styleWithCSS', false);
24647             }catch(e){}
24648         }
24649         this.owner.fireEvent('activate', this);
24650     },
24651
24652     // private
24653     adjustFont: function(btn){
24654         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24655         //if(Roo.isSafari){ // safari
24656         //    adjust *= 2;
24657        // }
24658         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24659         if(Roo.isSafari){ // safari
24660             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24661             v =  (v < 10) ? 10 : v;
24662             v =  (v > 48) ? 48 : v;
24663             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24664             
24665         }
24666         
24667         
24668         v = Math.max(1, v+adjust);
24669         
24670         this.execCmd('FontSize', v  );
24671     },
24672
24673     onEditorEvent : function(e)
24674     {
24675         this.owner.fireEvent('editorevent', this, e);
24676       //  this.updateToolbar();
24677         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24678     },
24679
24680     insertTag : function(tg)
24681     {
24682         // could be a bit smarter... -> wrap the current selected tRoo..
24683         if (tg.toLowerCase() == 'span' ||
24684             tg.toLowerCase() == 'code' ||
24685             tg.toLowerCase() == 'sup' ||
24686             tg.toLowerCase() == 'sub' 
24687             ) {
24688             
24689             range = this.createRange(this.getSelection());
24690             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24691             wrappingNode.appendChild(range.extractContents());
24692             range.insertNode(wrappingNode);
24693
24694             return;
24695             
24696             
24697             
24698         }
24699         this.execCmd("formatblock",   tg);
24700         
24701     },
24702     
24703     insertText : function(txt)
24704     {
24705         
24706         
24707         var range = this.createRange();
24708         range.deleteContents();
24709                //alert(Sender.getAttribute('label'));
24710                
24711         range.insertNode(this.doc.createTextNode(txt));
24712     } ,
24713     
24714      
24715
24716     /**
24717      * Executes a Midas editor command on the editor document and performs necessary focus and
24718      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24719      * @param {String} cmd The Midas command
24720      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24721      */
24722     relayCmd : function(cmd, value){
24723         this.win.focus();
24724         this.execCmd(cmd, value);
24725         this.owner.fireEvent('editorevent', this);
24726         //this.updateToolbar();
24727         this.owner.deferFocus();
24728     },
24729
24730     /**
24731      * Executes a Midas editor command directly on the editor document.
24732      * For visual commands, you should use {@link #relayCmd} instead.
24733      * <b>This should only be called after the editor is initialized.</b>
24734      * @param {String} cmd The Midas command
24735      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24736      */
24737     execCmd : function(cmd, value){
24738         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24739         this.syncValue();
24740     },
24741  
24742  
24743    
24744     /**
24745      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24746      * to insert tRoo.
24747      * @param {String} text | dom node.. 
24748      */
24749     insertAtCursor : function(text)
24750     {
24751         
24752         if(!this.activated){
24753             return;
24754         }
24755         /*
24756         if(Roo.isIE){
24757             this.win.focus();
24758             var r = this.doc.selection.createRange();
24759             if(r){
24760                 r.collapse(true);
24761                 r.pasteHTML(text);
24762                 this.syncValue();
24763                 this.deferFocus();
24764             
24765             }
24766             return;
24767         }
24768         */
24769         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24770             this.win.focus();
24771             
24772             
24773             // from jquery ui (MIT licenced)
24774             var range, node;
24775             var win = this.win;
24776             
24777             if (win.getSelection && win.getSelection().getRangeAt) {
24778                 range = win.getSelection().getRangeAt(0);
24779                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24780                 range.insertNode(node);
24781             } else if (win.document.selection && win.document.selection.createRange) {
24782                 // no firefox support
24783                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24784                 win.document.selection.createRange().pasteHTML(txt);
24785             } else {
24786                 // no firefox support
24787                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24788                 this.execCmd('InsertHTML', txt);
24789             } 
24790             
24791             this.syncValue();
24792             
24793             this.deferFocus();
24794         }
24795     },
24796  // private
24797     mozKeyPress : function(e){
24798         if(e.ctrlKey){
24799             var c = e.getCharCode(), cmd;
24800           
24801             if(c > 0){
24802                 c = String.fromCharCode(c).toLowerCase();
24803                 switch(c){
24804                     case 'b':
24805                         cmd = 'bold';
24806                         break;
24807                     case 'i':
24808                         cmd = 'italic';
24809                         break;
24810                     
24811                     case 'u':
24812                         cmd = 'underline';
24813                         break;
24814                     
24815                     case 'v':
24816                         this.cleanUpPaste.defer(100, this);
24817                         return;
24818                         
24819                 }
24820                 if(cmd){
24821                     this.win.focus();
24822                     this.execCmd(cmd);
24823                     this.deferFocus();
24824                     e.preventDefault();
24825                 }
24826                 
24827             }
24828         }
24829     },
24830
24831     // private
24832     fixKeys : function(){ // load time branching for fastest keydown performance
24833         if(Roo.isIE){
24834             return function(e){
24835                 var k = e.getKey(), r;
24836                 if(k == e.TAB){
24837                     e.stopEvent();
24838                     r = this.doc.selection.createRange();
24839                     if(r){
24840                         r.collapse(true);
24841                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24842                         this.deferFocus();
24843                     }
24844                     return;
24845                 }
24846                 
24847                 if(k == e.ENTER){
24848                     r = this.doc.selection.createRange();
24849                     if(r){
24850                         var target = r.parentElement();
24851                         if(!target || target.tagName.toLowerCase() != 'li'){
24852                             e.stopEvent();
24853                             r.pasteHTML('<br />');
24854                             r.collapse(false);
24855                             r.select();
24856                         }
24857                     }
24858                 }
24859                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24860                     this.cleanUpPaste.defer(100, this);
24861                     return;
24862                 }
24863                 
24864                 
24865             };
24866         }else if(Roo.isOpera){
24867             return function(e){
24868                 var k = e.getKey();
24869                 if(k == e.TAB){
24870                     e.stopEvent();
24871                     this.win.focus();
24872                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24873                     this.deferFocus();
24874                 }
24875                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24876                     this.cleanUpPaste.defer(100, this);
24877                     return;
24878                 }
24879                 
24880             };
24881         }else if(Roo.isSafari){
24882             return function(e){
24883                 var k = e.getKey();
24884                 
24885                 if(k == e.TAB){
24886                     e.stopEvent();
24887                     this.execCmd('InsertText','\t');
24888                     this.deferFocus();
24889                     return;
24890                 }
24891                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24892                     this.cleanUpPaste.defer(100, this);
24893                     return;
24894                 }
24895                 
24896              };
24897         }
24898     }(),
24899     
24900     getAllAncestors: function()
24901     {
24902         var p = this.getSelectedNode();
24903         var a = [];
24904         if (!p) {
24905             a.push(p); // push blank onto stack..
24906             p = this.getParentElement();
24907         }
24908         
24909         
24910         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24911             a.push(p);
24912             p = p.parentNode;
24913         }
24914         a.push(this.doc.body);
24915         return a;
24916     },
24917     lastSel : false,
24918     lastSelNode : false,
24919     
24920     
24921     getSelection : function() 
24922     {
24923         this.assignDocWin();
24924         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24925     },
24926     
24927     getSelectedNode: function() 
24928     {
24929         // this may only work on Gecko!!!
24930         
24931         // should we cache this!!!!
24932         
24933         
24934         
24935          
24936         var range = this.createRange(this.getSelection()).cloneRange();
24937         
24938         if (Roo.isIE) {
24939             var parent = range.parentElement();
24940             while (true) {
24941                 var testRange = range.duplicate();
24942                 testRange.moveToElementText(parent);
24943                 if (testRange.inRange(range)) {
24944                     break;
24945                 }
24946                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24947                     break;
24948                 }
24949                 parent = parent.parentElement;
24950             }
24951             return parent;
24952         }
24953         
24954         // is ancestor a text element.
24955         var ac =  range.commonAncestorContainer;
24956         if (ac.nodeType == 3) {
24957             ac = ac.parentNode;
24958         }
24959         
24960         var ar = ac.childNodes;
24961          
24962         var nodes = [];
24963         var other_nodes = [];
24964         var has_other_nodes = false;
24965         for (var i=0;i<ar.length;i++) {
24966             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24967                 continue;
24968             }
24969             // fullly contained node.
24970             
24971             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24972                 nodes.push(ar[i]);
24973                 continue;
24974             }
24975             
24976             // probably selected..
24977             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24978                 other_nodes.push(ar[i]);
24979                 continue;
24980             }
24981             // outer..
24982             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24983                 continue;
24984             }
24985             
24986             
24987             has_other_nodes = true;
24988         }
24989         if (!nodes.length && other_nodes.length) {
24990             nodes= other_nodes;
24991         }
24992         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24993             return false;
24994         }
24995         
24996         return nodes[0];
24997     },
24998     createRange: function(sel)
24999     {
25000         // this has strange effects when using with 
25001         // top toolbar - not sure if it's a great idea.
25002         //this.editor.contentWindow.focus();
25003         if (typeof sel != "undefined") {
25004             try {
25005                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
25006             } catch(e) {
25007                 return this.doc.createRange();
25008             }
25009         } else {
25010             return this.doc.createRange();
25011         }
25012     },
25013     getParentElement: function()
25014     {
25015         
25016         this.assignDocWin();
25017         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
25018         
25019         var range = this.createRange(sel);
25020          
25021         try {
25022             var p = range.commonAncestorContainer;
25023             while (p.nodeType == 3) { // text node
25024                 p = p.parentNode;
25025             }
25026             return p;
25027         } catch (e) {
25028             return null;
25029         }
25030     
25031     },
25032     /***
25033      *
25034      * Range intersection.. the hard stuff...
25035      *  '-1' = before
25036      *  '0' = hits..
25037      *  '1' = after.
25038      *         [ -- selected range --- ]
25039      *   [fail]                        [fail]
25040      *
25041      *    basically..
25042      *      if end is before start or  hits it. fail.
25043      *      if start is after end or hits it fail.
25044      *
25045      *   if either hits (but other is outside. - then it's not 
25046      *   
25047      *    
25048      **/
25049     
25050     
25051     // @see http://www.thismuchiknow.co.uk/?p=64.
25052     rangeIntersectsNode : function(range, node)
25053     {
25054         var nodeRange = node.ownerDocument.createRange();
25055         try {
25056             nodeRange.selectNode(node);
25057         } catch (e) {
25058             nodeRange.selectNodeContents(node);
25059         }
25060     
25061         var rangeStartRange = range.cloneRange();
25062         rangeStartRange.collapse(true);
25063     
25064         var rangeEndRange = range.cloneRange();
25065         rangeEndRange.collapse(false);
25066     
25067         var nodeStartRange = nodeRange.cloneRange();
25068         nodeStartRange.collapse(true);
25069     
25070         var nodeEndRange = nodeRange.cloneRange();
25071         nodeEndRange.collapse(false);
25072     
25073         return rangeStartRange.compareBoundaryPoints(
25074                  Range.START_TO_START, nodeEndRange) == -1 &&
25075                rangeEndRange.compareBoundaryPoints(
25076                  Range.START_TO_START, nodeStartRange) == 1;
25077         
25078          
25079     },
25080     rangeCompareNode : function(range, node)
25081     {
25082         var nodeRange = node.ownerDocument.createRange();
25083         try {
25084             nodeRange.selectNode(node);
25085         } catch (e) {
25086             nodeRange.selectNodeContents(node);
25087         }
25088         
25089         
25090         range.collapse(true);
25091     
25092         nodeRange.collapse(true);
25093      
25094         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25095         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25096          
25097         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25098         
25099         var nodeIsBefore   =  ss == 1;
25100         var nodeIsAfter    = ee == -1;
25101         
25102         if (nodeIsBefore && nodeIsAfter) {
25103             return 0; // outer
25104         }
25105         if (!nodeIsBefore && nodeIsAfter) {
25106             return 1; //right trailed.
25107         }
25108         
25109         if (nodeIsBefore && !nodeIsAfter) {
25110             return 2;  // left trailed.
25111         }
25112         // fully contined.
25113         return 3;
25114     },
25115
25116     // private? - in a new class?
25117     cleanUpPaste :  function()
25118     {
25119         // cleans up the whole document..
25120         Roo.log('cleanuppaste');
25121         
25122         this.cleanUpChildren(this.doc.body);
25123         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25124         if (clean != this.doc.body.innerHTML) {
25125             this.doc.body.innerHTML = clean;
25126         }
25127         
25128     },
25129     
25130     cleanWordChars : function(input) {// change the chars to hex code
25131         var he = Roo.HtmlEditorCore;
25132         
25133         var output = input;
25134         Roo.each(he.swapCodes, function(sw) { 
25135             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25136             
25137             output = output.replace(swapper, sw[1]);
25138         });
25139         
25140         return output;
25141     },
25142     
25143     
25144     cleanUpChildren : function (n)
25145     {
25146         if (!n.childNodes.length) {
25147             return;
25148         }
25149         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25150            this.cleanUpChild(n.childNodes[i]);
25151         }
25152     },
25153     
25154     
25155         
25156     
25157     cleanUpChild : function (node)
25158     {
25159         var ed = this;
25160         //console.log(node);
25161         if (node.nodeName == "#text") {
25162             // clean up silly Windows -- stuff?
25163             return; 
25164         }
25165         if (node.nodeName == "#comment") {
25166             node.parentNode.removeChild(node);
25167             // clean up silly Windows -- stuff?
25168             return; 
25169         }
25170         var lcname = node.tagName.toLowerCase();
25171         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25172         // whitelist of tags..
25173         
25174         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25175             // remove node.
25176             node.parentNode.removeChild(node);
25177             return;
25178             
25179         }
25180         
25181         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25182         
25183         // spans with no attributes - just remove them..
25184         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25185             remove_keep_children = true;
25186         }
25187         
25188         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25189         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25190         
25191         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25192         //    remove_keep_children = true;
25193         //}
25194         
25195         if (remove_keep_children) {
25196             this.cleanUpChildren(node);
25197             // inserts everything just before this node...
25198             while (node.childNodes.length) {
25199                 var cn = node.childNodes[0];
25200                 node.removeChild(cn);
25201                 node.parentNode.insertBefore(cn, node);
25202             }
25203             node.parentNode.removeChild(node);
25204             return;
25205         }
25206         
25207         if (!node.attributes || !node.attributes.length) {
25208             
25209           
25210             
25211             
25212             this.cleanUpChildren(node);
25213             return;
25214         }
25215         
25216         function cleanAttr(n,v)
25217         {
25218             
25219             if (v.match(/^\./) || v.match(/^\//)) {
25220                 return;
25221             }
25222             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25223                 return;
25224             }
25225             if (v.match(/^#/)) {
25226                 return;
25227             }
25228             if (v.match(/^\{/)) { // allow template editing.
25229                 return;
25230             }
25231 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25232             node.removeAttribute(n);
25233             
25234         }
25235         
25236         var cwhite = this.cwhite;
25237         var cblack = this.cblack;
25238             
25239         function cleanStyle(n,v)
25240         {
25241             if (v.match(/expression/)) { //XSS?? should we even bother..
25242                 node.removeAttribute(n);
25243                 return;
25244             }
25245             
25246             var parts = v.split(/;/);
25247             var clean = [];
25248             
25249             Roo.each(parts, function(p) {
25250                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25251                 if (!p.length) {
25252                     return true;
25253                 }
25254                 var l = p.split(':').shift().replace(/\s+/g,'');
25255                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25256                 
25257                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25258 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25259                     //node.removeAttribute(n);
25260                     return true;
25261                 }
25262                 //Roo.log()
25263                 // only allow 'c whitelisted system attributes'
25264                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25265 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25266                     //node.removeAttribute(n);
25267                     return true;
25268                 }
25269                 
25270                 
25271                  
25272                 
25273                 clean.push(p);
25274                 return true;
25275             });
25276             if (clean.length) { 
25277                 node.setAttribute(n, clean.join(';'));
25278             } else {
25279                 node.removeAttribute(n);
25280             }
25281             
25282         }
25283         
25284         
25285         for (var i = node.attributes.length-1; i > -1 ; i--) {
25286             var a = node.attributes[i];
25287             //console.log(a);
25288             
25289             if (a.name.toLowerCase().substr(0,2)=='on')  {
25290                 node.removeAttribute(a.name);
25291                 continue;
25292             }
25293             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25294                 node.removeAttribute(a.name);
25295                 continue;
25296             }
25297             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25298                 cleanAttr(a.name,a.value); // fixme..
25299                 continue;
25300             }
25301             if (a.name == 'style') {
25302                 cleanStyle(a.name,a.value);
25303                 continue;
25304             }
25305             /// clean up MS crap..
25306             // tecnically this should be a list of valid class'es..
25307             
25308             
25309             if (a.name == 'class') {
25310                 if (a.value.match(/^Mso/)) {
25311                     node.removeAttribute('class');
25312                 }
25313                 
25314                 if (a.value.match(/^body$/)) {
25315                     node.removeAttribute('class');
25316                 }
25317                 continue;
25318             }
25319             
25320             // style cleanup!?
25321             // class cleanup?
25322             
25323         }
25324         
25325         
25326         this.cleanUpChildren(node);
25327         
25328         
25329     },
25330     
25331     /**
25332      * Clean up MS wordisms...
25333      */
25334     cleanWord : function(node)
25335     {
25336         if (!node) {
25337             this.cleanWord(this.doc.body);
25338             return;
25339         }
25340         
25341         if(
25342                 node.nodeName == 'SPAN' &&
25343                 !node.hasAttributes() &&
25344                 node.childNodes.length == 1 &&
25345                 node.firstChild.nodeName == "#text"  
25346         ) {
25347             var textNode = node.firstChild;
25348             node.removeChild(textNode);
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.insertBefore(textNode, node);
25353             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25354                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25355             }
25356             node.parentNode.removeChild(node);
25357         }
25358         
25359         if (node.nodeName == "#text") {
25360             // clean up silly Windows -- stuff?
25361             return; 
25362         }
25363         if (node.nodeName == "#comment") {
25364             node.parentNode.removeChild(node);
25365             // clean up silly Windows -- stuff?
25366             return; 
25367         }
25368         
25369         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25370             node.parentNode.removeChild(node);
25371             return;
25372         }
25373         //Roo.log(node.tagName);
25374         // remove - but keep children..
25375         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25376             //Roo.log('-- removed');
25377             while (node.childNodes.length) {
25378                 var cn = node.childNodes[0];
25379                 node.removeChild(cn);
25380                 node.parentNode.insertBefore(cn, node);
25381                 // move node to parent - and clean it..
25382                 this.cleanWord(cn);
25383             }
25384             node.parentNode.removeChild(node);
25385             /// no need to iterate chidlren = it's got none..
25386             //this.iterateChildren(node, this.cleanWord);
25387             return;
25388         }
25389         // clean styles
25390         if (node.className.length) {
25391             
25392             var cn = node.className.split(/\W+/);
25393             var cna = [];
25394             Roo.each(cn, function(cls) {
25395                 if (cls.match(/Mso[a-zA-Z]+/)) {
25396                     return;
25397                 }
25398                 cna.push(cls);
25399             });
25400             node.className = cna.length ? cna.join(' ') : '';
25401             if (!cna.length) {
25402                 node.removeAttribute("class");
25403             }
25404         }
25405         
25406         if (node.hasAttribute("lang")) {
25407             node.removeAttribute("lang");
25408         }
25409         
25410         if (node.hasAttribute("style")) {
25411             
25412             var styles = node.getAttribute("style").split(";");
25413             var nstyle = [];
25414             Roo.each(styles, function(s) {
25415                 if (!s.match(/:/)) {
25416                     return;
25417                 }
25418                 var kv = s.split(":");
25419                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25420                     return;
25421                 }
25422                 // what ever is left... we allow.
25423                 nstyle.push(s);
25424             });
25425             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25426             if (!nstyle.length) {
25427                 node.removeAttribute('style');
25428             }
25429         }
25430         this.iterateChildren(node, this.cleanWord);
25431         
25432         
25433         
25434     },
25435     /**
25436      * iterateChildren of a Node, calling fn each time, using this as the scole..
25437      * @param {DomNode} node node to iterate children of.
25438      * @param {Function} fn method of this class to call on each item.
25439      */
25440     iterateChildren : function(node, fn)
25441     {
25442         if (!node.childNodes.length) {
25443                 return;
25444         }
25445         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25446            fn.call(this, node.childNodes[i])
25447         }
25448     },
25449     
25450     
25451     /**
25452      * cleanTableWidths.
25453      *
25454      * Quite often pasting from word etc.. results in tables with column and widths.
25455      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25456      *
25457      */
25458     cleanTableWidths : function(node)
25459     {
25460          
25461          
25462         if (!node) {
25463             this.cleanTableWidths(this.doc.body);
25464             return;
25465         }
25466         
25467         // ignore list...
25468         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25469             return; 
25470         }
25471         Roo.log(node.tagName);
25472         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25473             this.iterateChildren(node, this.cleanTableWidths);
25474             return;
25475         }
25476         if (node.hasAttribute('width')) {
25477             node.removeAttribute('width');
25478         }
25479         
25480          
25481         if (node.hasAttribute("style")) {
25482             // pretty basic...
25483             
25484             var styles = node.getAttribute("style").split(";");
25485             var nstyle = [];
25486             Roo.each(styles, function(s) {
25487                 if (!s.match(/:/)) {
25488                     return;
25489                 }
25490                 var kv = s.split(":");
25491                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25492                     return;
25493                 }
25494                 // what ever is left... we allow.
25495                 nstyle.push(s);
25496             });
25497             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25498             if (!nstyle.length) {
25499                 node.removeAttribute('style');
25500             }
25501         }
25502         
25503         this.iterateChildren(node, this.cleanTableWidths);
25504         
25505         
25506     },
25507     
25508     
25509     
25510     
25511     domToHTML : function(currentElement, depth, nopadtext) {
25512         
25513         depth = depth || 0;
25514         nopadtext = nopadtext || false;
25515     
25516         if (!currentElement) {
25517             return this.domToHTML(this.doc.body);
25518         }
25519         
25520         //Roo.log(currentElement);
25521         var j;
25522         var allText = false;
25523         var nodeName = currentElement.nodeName;
25524         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25525         
25526         if  (nodeName == '#text') {
25527             
25528             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25529         }
25530         
25531         
25532         var ret = '';
25533         if (nodeName != 'BODY') {
25534              
25535             var i = 0;
25536             // Prints the node tagName, such as <A>, <IMG>, etc
25537             if (tagName) {
25538                 var attr = [];
25539                 for(i = 0; i < currentElement.attributes.length;i++) {
25540                     // quoting?
25541                     var aname = currentElement.attributes.item(i).name;
25542                     if (!currentElement.attributes.item(i).value.length) {
25543                         continue;
25544                     }
25545                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25546                 }
25547                 
25548                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25549             } 
25550             else {
25551                 
25552                 // eack
25553             }
25554         } else {
25555             tagName = false;
25556         }
25557         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25558             return ret;
25559         }
25560         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25561             nopadtext = true;
25562         }
25563         
25564         
25565         // Traverse the tree
25566         i = 0;
25567         var currentElementChild = currentElement.childNodes.item(i);
25568         var allText = true;
25569         var innerHTML  = '';
25570         lastnode = '';
25571         while (currentElementChild) {
25572             // Formatting code (indent the tree so it looks nice on the screen)
25573             var nopad = nopadtext;
25574             if (lastnode == 'SPAN') {
25575                 nopad  = true;
25576             }
25577             // text
25578             if  (currentElementChild.nodeName == '#text') {
25579                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25580                 toadd = nopadtext ? toadd : toadd.trim();
25581                 if (!nopad && toadd.length > 80) {
25582                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25583                 }
25584                 innerHTML  += toadd;
25585                 
25586                 i++;
25587                 currentElementChild = currentElement.childNodes.item(i);
25588                 lastNode = '';
25589                 continue;
25590             }
25591             allText = false;
25592             
25593             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25594                 
25595             // Recursively traverse the tree structure of the child node
25596             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25597             lastnode = currentElementChild.nodeName;
25598             i++;
25599             currentElementChild=currentElement.childNodes.item(i);
25600         }
25601         
25602         ret += innerHTML;
25603         
25604         if (!allText) {
25605                 // The remaining code is mostly for formatting the tree
25606             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25607         }
25608         
25609         
25610         if (tagName) {
25611             ret+= "</"+tagName+">";
25612         }
25613         return ret;
25614         
25615     },
25616         
25617     applyBlacklists : function()
25618     {
25619         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25620         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25621         
25622         this.white = [];
25623         this.black = [];
25624         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25625             if (b.indexOf(tag) > -1) {
25626                 return;
25627             }
25628             this.white.push(tag);
25629             
25630         }, this);
25631         
25632         Roo.each(w, function(tag) {
25633             if (b.indexOf(tag) > -1) {
25634                 return;
25635             }
25636             if (this.white.indexOf(tag) > -1) {
25637                 return;
25638             }
25639             this.white.push(tag);
25640             
25641         }, this);
25642         
25643         
25644         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25645             if (w.indexOf(tag) > -1) {
25646                 return;
25647             }
25648             this.black.push(tag);
25649             
25650         }, this);
25651         
25652         Roo.each(b, function(tag) {
25653             if (w.indexOf(tag) > -1) {
25654                 return;
25655             }
25656             if (this.black.indexOf(tag) > -1) {
25657                 return;
25658             }
25659             this.black.push(tag);
25660             
25661         }, this);
25662         
25663         
25664         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25665         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25666         
25667         this.cwhite = [];
25668         this.cblack = [];
25669         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25670             if (b.indexOf(tag) > -1) {
25671                 return;
25672             }
25673             this.cwhite.push(tag);
25674             
25675         }, this);
25676         
25677         Roo.each(w, function(tag) {
25678             if (b.indexOf(tag) > -1) {
25679                 return;
25680             }
25681             if (this.cwhite.indexOf(tag) > -1) {
25682                 return;
25683             }
25684             this.cwhite.push(tag);
25685             
25686         }, this);
25687         
25688         
25689         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25690             if (w.indexOf(tag) > -1) {
25691                 return;
25692             }
25693             this.cblack.push(tag);
25694             
25695         }, this);
25696         
25697         Roo.each(b, function(tag) {
25698             if (w.indexOf(tag) > -1) {
25699                 return;
25700             }
25701             if (this.cblack.indexOf(tag) > -1) {
25702                 return;
25703             }
25704             this.cblack.push(tag);
25705             
25706         }, this);
25707     },
25708     
25709     setStylesheets : function(stylesheets)
25710     {
25711         if(typeof(stylesheets) == 'string'){
25712             Roo.get(this.iframe.contentDocument.head).createChild({
25713                 tag : 'link',
25714                 rel : 'stylesheet',
25715                 type : 'text/css',
25716                 href : stylesheets
25717             });
25718             
25719             return;
25720         }
25721         var _this = this;
25722      
25723         Roo.each(stylesheets, function(s) {
25724             if(!s.length){
25725                 return;
25726             }
25727             
25728             Roo.get(_this.iframe.contentDocument.head).createChild({
25729                 tag : 'link',
25730                 rel : 'stylesheet',
25731                 type : 'text/css',
25732                 href : s
25733             });
25734         });
25735
25736         
25737     },
25738     
25739     removeStylesheets : function()
25740     {
25741         var _this = this;
25742         
25743         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25744             s.remove();
25745         });
25746     },
25747     
25748     setStyle : function(style)
25749     {
25750         Roo.get(this.iframe.contentDocument.head).createChild({
25751             tag : 'style',
25752             type : 'text/css',
25753             html : style
25754         });
25755
25756         return;
25757     }
25758     
25759     // hide stuff that is not compatible
25760     /**
25761      * @event blur
25762      * @hide
25763      */
25764     /**
25765      * @event change
25766      * @hide
25767      */
25768     /**
25769      * @event focus
25770      * @hide
25771      */
25772     /**
25773      * @event specialkey
25774      * @hide
25775      */
25776     /**
25777      * @cfg {String} fieldClass @hide
25778      */
25779     /**
25780      * @cfg {String} focusClass @hide
25781      */
25782     /**
25783      * @cfg {String} autoCreate @hide
25784      */
25785     /**
25786      * @cfg {String} inputType @hide
25787      */
25788     /**
25789      * @cfg {String} invalidClass @hide
25790      */
25791     /**
25792      * @cfg {String} invalidText @hide
25793      */
25794     /**
25795      * @cfg {String} msgFx @hide
25796      */
25797     /**
25798      * @cfg {String} validateOnBlur @hide
25799      */
25800 });
25801
25802 Roo.HtmlEditorCore.white = [
25803         'area', 'br', 'img', 'input', 'hr', 'wbr',
25804         
25805        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25806        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25807        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25808        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25809        'table',   'ul',         'xmp', 
25810        
25811        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25812       'thead',   'tr', 
25813      
25814       'dir', 'menu', 'ol', 'ul', 'dl',
25815        
25816       'embed',  'object'
25817 ];
25818
25819
25820 Roo.HtmlEditorCore.black = [
25821     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25822         'applet', // 
25823         'base',   'basefont', 'bgsound', 'blink',  'body', 
25824         'frame',  'frameset', 'head',    'html',   'ilayer', 
25825         'iframe', 'layer',  'link',     'meta',    'object',   
25826         'script', 'style' ,'title',  'xml' // clean later..
25827 ];
25828 Roo.HtmlEditorCore.clean = [
25829     'script', 'style', 'title', 'xml'
25830 ];
25831 Roo.HtmlEditorCore.remove = [
25832     'font'
25833 ];
25834 // attributes..
25835
25836 Roo.HtmlEditorCore.ablack = [
25837     'on'
25838 ];
25839     
25840 Roo.HtmlEditorCore.aclean = [ 
25841     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25842 ];
25843
25844 // protocols..
25845 Roo.HtmlEditorCore.pwhite= [
25846         'http',  'https',  'mailto'
25847 ];
25848
25849 // white listed style attributes.
25850 Roo.HtmlEditorCore.cwhite= [
25851       //  'text-align', /// default is to allow most things..
25852       
25853          
25854 //        'font-size'//??
25855 ];
25856
25857 // black listed style attributes.
25858 Roo.HtmlEditorCore.cblack= [
25859       //  'font-size' -- this can be set by the project 
25860 ];
25861
25862
25863 Roo.HtmlEditorCore.swapCodes   =[ 
25864     [    8211, "&#8211;" ], 
25865     [    8212, "&#8212;" ], 
25866     [    8216,  "'" ],  
25867     [    8217, "'" ],  
25868     [    8220, '"' ],  
25869     [    8221, '"' ],  
25870     [    8226, "*" ],  
25871     [    8230, "..." ]
25872 ]; 
25873
25874     /*
25875  * - LGPL
25876  *
25877  * HtmlEditor
25878  * 
25879  */
25880
25881 /**
25882  * @class Roo.bootstrap.HtmlEditor
25883  * @extends Roo.bootstrap.TextArea
25884  * Bootstrap HtmlEditor class
25885
25886  * @constructor
25887  * Create a new HtmlEditor
25888  * @param {Object} config The config object
25889  */
25890
25891 Roo.bootstrap.HtmlEditor = function(config){
25892     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25893     if (!this.toolbars) {
25894         this.toolbars = [];
25895     }
25896     
25897     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25898     this.addEvents({
25899             /**
25900              * @event initialize
25901              * Fires when the editor is fully initialized (including the iframe)
25902              * @param {HtmlEditor} this
25903              */
25904             initialize: true,
25905             /**
25906              * @event activate
25907              * Fires when the editor is first receives the focus. Any insertion must wait
25908              * until after this event.
25909              * @param {HtmlEditor} this
25910              */
25911             activate: true,
25912              /**
25913              * @event beforesync
25914              * Fires before the textarea is updated with content from the editor iframe. Return false
25915              * to cancel the sync.
25916              * @param {HtmlEditor} this
25917              * @param {String} html
25918              */
25919             beforesync: true,
25920              /**
25921              * @event beforepush
25922              * Fires before the iframe editor is updated with content from the textarea. Return false
25923              * to cancel the push.
25924              * @param {HtmlEditor} this
25925              * @param {String} html
25926              */
25927             beforepush: true,
25928              /**
25929              * @event sync
25930              * Fires when the textarea is updated with content from the editor iframe.
25931              * @param {HtmlEditor} this
25932              * @param {String} html
25933              */
25934             sync: true,
25935              /**
25936              * @event push
25937              * Fires when the iframe editor is updated with content from the textarea.
25938              * @param {HtmlEditor} this
25939              * @param {String} html
25940              */
25941             push: true,
25942              /**
25943              * @event editmodechange
25944              * Fires when the editor switches edit modes
25945              * @param {HtmlEditor} this
25946              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25947              */
25948             editmodechange: true,
25949             /**
25950              * @event editorevent
25951              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25952              * @param {HtmlEditor} this
25953              */
25954             editorevent: true,
25955             /**
25956              * @event firstfocus
25957              * Fires when on first focus - needed by toolbars..
25958              * @param {HtmlEditor} this
25959              */
25960             firstfocus: true,
25961             /**
25962              * @event autosave
25963              * Auto save the htmlEditor value as a file into Events
25964              * @param {HtmlEditor} this
25965              */
25966             autosave: true,
25967             /**
25968              * @event savedpreview
25969              * preview the saved version of htmlEditor
25970              * @param {HtmlEditor} this
25971              */
25972             savedpreview: true
25973         });
25974 };
25975
25976
25977 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25978     
25979     
25980       /**
25981      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25982      */
25983     toolbars : false,
25984     
25985      /**
25986     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25987     */
25988     btns : [],
25989    
25990      /**
25991      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25992      *                        Roo.resizable.
25993      */
25994     resizable : false,
25995      /**
25996      * @cfg {Number} height (in pixels)
25997      */   
25998     height: 300,
25999    /**
26000      * @cfg {Number} width (in pixels)
26001      */   
26002     width: false,
26003     
26004     /**
26005      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
26006      * 
26007      */
26008     stylesheets: false,
26009     
26010     // id of frame..
26011     frameId: false,
26012     
26013     // private properties
26014     validationEvent : false,
26015     deferHeight: true,
26016     initialized : false,
26017     activated : false,
26018     
26019     onFocus : Roo.emptyFn,
26020     iframePad:3,
26021     hideMode:'offsets',
26022     
26023     tbContainer : false,
26024     
26025     bodyCls : '',
26026     
26027     toolbarContainer :function() {
26028         return this.wrap.select('.x-html-editor-tb',true).first();
26029     },
26030
26031     /**
26032      * Protected method that will not generally be called directly. It
26033      * is called when the editor creates its toolbar. Override this method if you need to
26034      * add custom toolbar buttons.
26035      * @param {HtmlEditor} editor
26036      */
26037     createToolbar : function(){
26038         Roo.log('renewing');
26039         Roo.log("create toolbars");
26040         
26041         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
26042         this.toolbars[0].render(this.toolbarContainer());
26043         
26044         return;
26045         
26046 //        if (!editor.toolbars || !editor.toolbars.length) {
26047 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
26048 //        }
26049 //        
26050 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26051 //            editor.toolbars[i] = Roo.factory(
26052 //                    typeof(editor.toolbars[i]) == 'string' ?
26053 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26054 //                Roo.bootstrap.HtmlEditor);
26055 //            editor.toolbars[i].init(editor);
26056 //        }
26057     },
26058
26059      
26060     // private
26061     onRender : function(ct, position)
26062     {
26063        // Roo.log("Call onRender: " + this.xtype);
26064         var _t = this;
26065         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26066       
26067         this.wrap = this.inputEl().wrap({
26068             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26069         });
26070         
26071         this.editorcore.onRender(ct, position);
26072          
26073         if (this.resizable) {
26074             this.resizeEl = new Roo.Resizable(this.wrap, {
26075                 pinned : true,
26076                 wrap: true,
26077                 dynamic : true,
26078                 minHeight : this.height,
26079                 height: this.height,
26080                 handles : this.resizable,
26081                 width: this.width,
26082                 listeners : {
26083                     resize : function(r, w, h) {
26084                         _t.onResize(w,h); // -something
26085                     }
26086                 }
26087             });
26088             
26089         }
26090         this.createToolbar(this);
26091        
26092         
26093         if(!this.width && this.resizable){
26094             this.setSize(this.wrap.getSize());
26095         }
26096         if (this.resizeEl) {
26097             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26098             // should trigger onReize..
26099         }
26100         
26101     },
26102
26103     // private
26104     onResize : function(w, h)
26105     {
26106         Roo.log('resize: ' +w + ',' + h );
26107         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26108         var ew = false;
26109         var eh = false;
26110         
26111         if(this.inputEl() ){
26112             if(typeof w == 'number'){
26113                 var aw = w - this.wrap.getFrameWidth('lr');
26114                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26115                 ew = aw;
26116             }
26117             if(typeof h == 'number'){
26118                  var tbh = -11;  // fixme it needs to tool bar size!
26119                 for (var i =0; i < this.toolbars.length;i++) {
26120                     // fixme - ask toolbars for heights?
26121                     tbh += this.toolbars[i].el.getHeight();
26122                     //if (this.toolbars[i].footer) {
26123                     //    tbh += this.toolbars[i].footer.el.getHeight();
26124                     //}
26125                 }
26126               
26127                 
26128                 
26129                 
26130                 
26131                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26132                 ah -= 5; // knock a few pixes off for look..
26133                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26134                 var eh = ah;
26135             }
26136         }
26137         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26138         this.editorcore.onResize(ew,eh);
26139         
26140     },
26141
26142     /**
26143      * Toggles the editor between standard and source edit mode.
26144      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26145      */
26146     toggleSourceEdit : function(sourceEditMode)
26147     {
26148         this.editorcore.toggleSourceEdit(sourceEditMode);
26149         
26150         if(this.editorcore.sourceEditMode){
26151             Roo.log('editor - showing textarea');
26152             
26153 //            Roo.log('in');
26154 //            Roo.log(this.syncValue());
26155             this.syncValue();
26156             this.inputEl().removeClass(['hide', 'x-hidden']);
26157             this.inputEl().dom.removeAttribute('tabIndex');
26158             this.inputEl().focus();
26159         }else{
26160             Roo.log('editor - hiding textarea');
26161 //            Roo.log('out')
26162 //            Roo.log(this.pushValue()); 
26163             this.pushValue();
26164             
26165             this.inputEl().addClass(['hide', 'x-hidden']);
26166             this.inputEl().dom.setAttribute('tabIndex', -1);
26167             //this.deferFocus();
26168         }
26169          
26170         if(this.resizable){
26171             this.setSize(this.wrap.getSize());
26172         }
26173         
26174         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26175     },
26176  
26177     // private (for BoxComponent)
26178     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26179
26180     // private (for BoxComponent)
26181     getResizeEl : function(){
26182         return this.wrap;
26183     },
26184
26185     // private (for BoxComponent)
26186     getPositionEl : function(){
26187         return this.wrap;
26188     },
26189
26190     // private
26191     initEvents : function(){
26192         this.originalValue = this.getValue();
26193     },
26194
26195 //    /**
26196 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26197 //     * @method
26198 //     */
26199 //    markInvalid : Roo.emptyFn,
26200 //    /**
26201 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26202 //     * @method
26203 //     */
26204 //    clearInvalid : Roo.emptyFn,
26205
26206     setValue : function(v){
26207         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26208         this.editorcore.pushValue();
26209     },
26210
26211      
26212     // private
26213     deferFocus : function(){
26214         this.focus.defer(10, this);
26215     },
26216
26217     // doc'ed in Field
26218     focus : function(){
26219         this.editorcore.focus();
26220         
26221     },
26222       
26223
26224     // private
26225     onDestroy : function(){
26226         
26227         
26228         
26229         if(this.rendered){
26230             
26231             for (var i =0; i < this.toolbars.length;i++) {
26232                 // fixme - ask toolbars for heights?
26233                 this.toolbars[i].onDestroy();
26234             }
26235             
26236             this.wrap.dom.innerHTML = '';
26237             this.wrap.remove();
26238         }
26239     },
26240
26241     // private
26242     onFirstFocus : function(){
26243         //Roo.log("onFirstFocus");
26244         this.editorcore.onFirstFocus();
26245          for (var i =0; i < this.toolbars.length;i++) {
26246             this.toolbars[i].onFirstFocus();
26247         }
26248         
26249     },
26250     
26251     // private
26252     syncValue : function()
26253     {   
26254         this.editorcore.syncValue();
26255     },
26256     
26257     pushValue : function()
26258     {   
26259         this.editorcore.pushValue();
26260     }
26261      
26262     
26263     // hide stuff that is not compatible
26264     /**
26265      * @event blur
26266      * @hide
26267      */
26268     /**
26269      * @event change
26270      * @hide
26271      */
26272     /**
26273      * @event focus
26274      * @hide
26275      */
26276     /**
26277      * @event specialkey
26278      * @hide
26279      */
26280     /**
26281      * @cfg {String} fieldClass @hide
26282      */
26283     /**
26284      * @cfg {String} focusClass @hide
26285      */
26286     /**
26287      * @cfg {String} autoCreate @hide
26288      */
26289     /**
26290      * @cfg {String} inputType @hide
26291      */
26292      
26293     /**
26294      * @cfg {String} invalidText @hide
26295      */
26296     /**
26297      * @cfg {String} msgFx @hide
26298      */
26299     /**
26300      * @cfg {String} validateOnBlur @hide
26301      */
26302 });
26303  
26304     
26305    
26306    
26307    
26308       
26309 Roo.namespace('Roo.bootstrap.htmleditor');
26310 /**
26311  * @class Roo.bootstrap.HtmlEditorToolbar1
26312  * Basic Toolbar
26313  * 
26314  * @example
26315  * Usage:
26316  *
26317  new Roo.bootstrap.HtmlEditor({
26318     ....
26319     toolbars : [
26320         new Roo.bootstrap.HtmlEditorToolbar1({
26321             disable : { fonts: 1 , format: 1, ..., ... , ...],
26322             btns : [ .... ]
26323         })
26324     }
26325      
26326  * 
26327  * @cfg {Object} disable List of elements to disable..
26328  * @cfg {Array} btns List of additional buttons.
26329  * 
26330  * 
26331  * NEEDS Extra CSS? 
26332  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26333  */
26334  
26335 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26336 {
26337     
26338     Roo.apply(this, config);
26339     
26340     // default disabled, based on 'good practice'..
26341     this.disable = this.disable || {};
26342     Roo.applyIf(this.disable, {
26343         fontSize : true,
26344         colors : true,
26345         specialElements : true
26346     });
26347     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26348     
26349     this.editor = config.editor;
26350     this.editorcore = config.editor.editorcore;
26351     
26352     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26353     
26354     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26355     // dont call parent... till later.
26356 }
26357 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26358      
26359     bar : true,
26360     
26361     editor : false,
26362     editorcore : false,
26363     
26364     
26365     formats : [
26366         "p" ,  
26367         "h1","h2","h3","h4","h5","h6", 
26368         "pre", "code", 
26369         "abbr", "acronym", "address", "cite", "samp", "var",
26370         'div','span'
26371     ],
26372     
26373     onRender : function(ct, position)
26374     {
26375        // Roo.log("Call onRender: " + this.xtype);
26376         
26377        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26378        Roo.log(this.el);
26379        this.el.dom.style.marginBottom = '0';
26380        var _this = this;
26381        var editorcore = this.editorcore;
26382        var editor= this.editor;
26383        
26384        var children = [];
26385        var btn = function(id,cmd , toggle, handler, html){
26386        
26387             var  event = toggle ? 'toggle' : 'click';
26388        
26389             var a = {
26390                 size : 'sm',
26391                 xtype: 'Button',
26392                 xns: Roo.bootstrap,
26393                 //glyphicon : id,
26394                 fa: id,
26395                 cmd : id || cmd,
26396                 enableToggle:toggle !== false,
26397                 html : html || '',
26398                 pressed : toggle ? false : null,
26399                 listeners : {}
26400             };
26401             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26402                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26403             };
26404             children.push(a);
26405             return a;
26406        }
26407        
26408     //    var cb_box = function...
26409         
26410         var style = {
26411                 xtype: 'Button',
26412                 size : 'sm',
26413                 xns: Roo.bootstrap,
26414                 fa : 'font',
26415                 //html : 'submit'
26416                 menu : {
26417                     xtype: 'Menu',
26418                     xns: Roo.bootstrap,
26419                     items:  []
26420                 }
26421         };
26422         Roo.each(this.formats, function(f) {
26423             style.menu.items.push({
26424                 xtype :'MenuItem',
26425                 xns: Roo.bootstrap,
26426                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26427                 tagname : f,
26428                 listeners : {
26429                     click : function()
26430                     {
26431                         editorcore.insertTag(this.tagname);
26432                         editor.focus();
26433                     }
26434                 }
26435                 
26436             });
26437         });
26438         children.push(style);   
26439         
26440         btn('bold',false,true);
26441         btn('italic',false,true);
26442         btn('align-left', 'justifyleft',true);
26443         btn('align-center', 'justifycenter',true);
26444         btn('align-right' , 'justifyright',true);
26445         btn('link', false, false, function(btn) {
26446             //Roo.log("create link?");
26447             var url = prompt(this.createLinkText, this.defaultLinkValue);
26448             if(url && url != 'http:/'+'/'){
26449                 this.editorcore.relayCmd('createlink', url);
26450             }
26451         }),
26452         btn('list','insertunorderedlist',true);
26453         btn('pencil', false,true, function(btn){
26454                 Roo.log(this);
26455                 this.toggleSourceEdit(btn.pressed);
26456         });
26457         
26458         if (this.editor.btns.length > 0) {
26459             for (var i = 0; i<this.editor.btns.length; i++) {
26460                 children.push(this.editor.btns[i]);
26461             }
26462         }
26463         
26464         /*
26465         var cog = {
26466                 xtype: 'Button',
26467                 size : 'sm',
26468                 xns: Roo.bootstrap,
26469                 glyphicon : 'cog',
26470                 //html : 'submit'
26471                 menu : {
26472                     xtype: 'Menu',
26473                     xns: Roo.bootstrap,
26474                     items:  []
26475                 }
26476         };
26477         
26478         cog.menu.items.push({
26479             xtype :'MenuItem',
26480             xns: Roo.bootstrap,
26481             html : Clean styles,
26482             tagname : f,
26483             listeners : {
26484                 click : function()
26485                 {
26486                     editorcore.insertTag(this.tagname);
26487                     editor.focus();
26488                 }
26489             }
26490             
26491         });
26492        */
26493         
26494          
26495        this.xtype = 'NavSimplebar';
26496         
26497         for(var i=0;i< children.length;i++) {
26498             
26499             this.buttons.add(this.addxtypeChild(children[i]));
26500             
26501         }
26502         
26503         editor.on('editorevent', this.updateToolbar, this);
26504     },
26505     onBtnClick : function(id)
26506     {
26507        this.editorcore.relayCmd(id);
26508        this.editorcore.focus();
26509     },
26510     
26511     /**
26512      * Protected method that will not generally be called directly. It triggers
26513      * a toolbar update by reading the markup state of the current selection in the editor.
26514      */
26515     updateToolbar: function(){
26516
26517         if(!this.editorcore.activated){
26518             this.editor.onFirstFocus(); // is this neeed?
26519             return;
26520         }
26521
26522         var btns = this.buttons; 
26523         var doc = this.editorcore.doc;
26524         btns.get('bold').setActive(doc.queryCommandState('bold'));
26525         btns.get('italic').setActive(doc.queryCommandState('italic'));
26526         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26527         
26528         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26529         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26530         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26531         
26532         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26533         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26534          /*
26535         
26536         var ans = this.editorcore.getAllAncestors();
26537         if (this.formatCombo) {
26538             
26539             
26540             var store = this.formatCombo.store;
26541             this.formatCombo.setValue("");
26542             for (var i =0; i < ans.length;i++) {
26543                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26544                     // select it..
26545                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26546                     break;
26547                 }
26548             }
26549         }
26550         
26551         
26552         
26553         // hides menus... - so this cant be on a menu...
26554         Roo.bootstrap.MenuMgr.hideAll();
26555         */
26556         Roo.bootstrap.MenuMgr.hideAll();
26557         //this.editorsyncValue();
26558     },
26559     onFirstFocus: function() {
26560         this.buttons.each(function(item){
26561            item.enable();
26562         });
26563     },
26564     toggleSourceEdit : function(sourceEditMode){
26565         
26566           
26567         if(sourceEditMode){
26568             Roo.log("disabling buttons");
26569            this.buttons.each( function(item){
26570                 if(item.cmd != 'pencil'){
26571                     item.disable();
26572                 }
26573             });
26574           
26575         }else{
26576             Roo.log("enabling buttons");
26577             if(this.editorcore.initialized){
26578                 this.buttons.each( function(item){
26579                     item.enable();
26580                 });
26581             }
26582             
26583         }
26584         Roo.log("calling toggole on editor");
26585         // tell the editor that it's been pressed..
26586         this.editor.toggleSourceEdit(sourceEditMode);
26587        
26588     }
26589 });
26590
26591
26592
26593
26594  
26595 /*
26596  * - LGPL
26597  */
26598
26599 /**
26600  * @class Roo.bootstrap.Markdown
26601  * @extends Roo.bootstrap.TextArea
26602  * Bootstrap Showdown editable area
26603  * @cfg {string} content
26604  * 
26605  * @constructor
26606  * Create a new Showdown
26607  */
26608
26609 Roo.bootstrap.Markdown = function(config){
26610     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26611    
26612 };
26613
26614 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26615     
26616     editing :false,
26617     
26618     initEvents : function()
26619     {
26620         
26621         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26622         this.markdownEl = this.el.createChild({
26623             cls : 'roo-markdown-area'
26624         });
26625         this.inputEl().addClass('d-none');
26626         if (this.getValue() == '') {
26627             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26628             
26629         } else {
26630             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26631         }
26632         this.markdownEl.on('click', this.toggleTextEdit, this);
26633         this.on('blur', this.toggleTextEdit, this);
26634         this.on('specialkey', this.resizeTextArea, this);
26635     },
26636     
26637     toggleTextEdit : function()
26638     {
26639         var sh = this.markdownEl.getHeight();
26640         this.inputEl().addClass('d-none');
26641         this.markdownEl.addClass('d-none');
26642         if (!this.editing) {
26643             // show editor?
26644             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26645             this.inputEl().removeClass('d-none');
26646             this.inputEl().focus();
26647             this.editing = true;
26648             return;
26649         }
26650         // show showdown...
26651         this.updateMarkdown();
26652         this.markdownEl.removeClass('d-none');
26653         this.editing = false;
26654         return;
26655     },
26656     updateMarkdown : function()
26657     {
26658         if (this.getValue() == '') {
26659             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26660             return;
26661         }
26662  
26663         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26664     },
26665     
26666     resizeTextArea: function () {
26667         
26668         var sh = 100;
26669         Roo.log([sh, this.getValue().split("\n").length * 30]);
26670         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26671     },
26672     setValue : function(val)
26673     {
26674         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26675         if (!this.editing) {
26676             this.updateMarkdown();
26677         }
26678         
26679     },
26680     focus : function()
26681     {
26682         if (!this.editing) {
26683             this.toggleTextEdit();
26684         }
26685         
26686     }
26687
26688
26689 });
26690 /**
26691  * @class Roo.bootstrap.Table.AbstractSelectionModel
26692  * @extends Roo.util.Observable
26693  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26694  * implemented by descendant classes.  This class should not be directly instantiated.
26695  * @constructor
26696  */
26697 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26698     this.locked = false;
26699     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26700 };
26701
26702
26703 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26704     /** @ignore Called by the grid automatically. Do not call directly. */
26705     init : function(grid){
26706         this.grid = grid;
26707         this.initEvents();
26708     },
26709
26710     /**
26711      * Locks the selections.
26712      */
26713     lock : function(){
26714         this.locked = true;
26715     },
26716
26717     /**
26718      * Unlocks the selections.
26719      */
26720     unlock : function(){
26721         this.locked = false;
26722     },
26723
26724     /**
26725      * Returns true if the selections are locked.
26726      * @return {Boolean}
26727      */
26728     isLocked : function(){
26729         return this.locked;
26730     },
26731     
26732     
26733     initEvents : function ()
26734     {
26735         
26736     }
26737 });
26738 /**
26739  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26740  * @class Roo.bootstrap.Table.RowSelectionModel
26741  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26742  * It supports multiple selections and keyboard selection/navigation. 
26743  * @constructor
26744  * @param {Object} config
26745  */
26746
26747 Roo.bootstrap.Table.RowSelectionModel = function(config){
26748     Roo.apply(this, config);
26749     this.selections = new Roo.util.MixedCollection(false, function(o){
26750         return o.id;
26751     });
26752
26753     this.last = false;
26754     this.lastActive = false;
26755
26756     this.addEvents({
26757         /**
26758              * @event selectionchange
26759              * Fires when the selection changes
26760              * @param {SelectionModel} this
26761              */
26762             "selectionchange" : true,
26763         /**
26764              * @event afterselectionchange
26765              * Fires after the selection changes (eg. by key press or clicking)
26766              * @param {SelectionModel} this
26767              */
26768             "afterselectionchange" : true,
26769         /**
26770              * @event beforerowselect
26771              * Fires when a row is selected being selected, return false to cancel.
26772              * @param {SelectionModel} this
26773              * @param {Number} rowIndex The selected index
26774              * @param {Boolean} keepExisting False if other selections will be cleared
26775              */
26776             "beforerowselect" : true,
26777         /**
26778              * @event rowselect
26779              * Fires when a row is selected.
26780              * @param {SelectionModel} this
26781              * @param {Number} rowIndex The selected index
26782              * @param {Roo.data.Record} r The record
26783              */
26784             "rowselect" : true,
26785         /**
26786              * @event rowdeselect
26787              * Fires when a row is deselected.
26788              * @param {SelectionModel} this
26789              * @param {Number} rowIndex The selected index
26790              */
26791         "rowdeselect" : true
26792     });
26793     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26794     this.locked = false;
26795  };
26796
26797 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26798     /**
26799      * @cfg {Boolean} singleSelect
26800      * True to allow selection of only one row at a time (defaults to false)
26801      */
26802     singleSelect : false,
26803
26804     // private
26805     initEvents : function()
26806     {
26807
26808         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26809         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26810         //}else{ // allow click to work like normal
26811          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26812         //}
26813         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26814         this.grid.on("rowclick", this.handleMouseDown, this);
26815         
26816         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26817             "up" : function(e){
26818                 if(!e.shiftKey){
26819                     this.selectPrevious(e.shiftKey);
26820                 }else if(this.last !== false && this.lastActive !== false){
26821                     var last = this.last;
26822                     this.selectRange(this.last,  this.lastActive-1);
26823                     this.grid.getView().focusRow(this.lastActive);
26824                     if(last !== false){
26825                         this.last = last;
26826                     }
26827                 }else{
26828                     this.selectFirstRow();
26829                 }
26830                 this.fireEvent("afterselectionchange", this);
26831             },
26832             "down" : function(e){
26833                 if(!e.shiftKey){
26834                     this.selectNext(e.shiftKey);
26835                 }else if(this.last !== false && this.lastActive !== false){
26836                     var last = this.last;
26837                     this.selectRange(this.last,  this.lastActive+1);
26838                     this.grid.getView().focusRow(this.lastActive);
26839                     if(last !== false){
26840                         this.last = last;
26841                     }
26842                 }else{
26843                     this.selectFirstRow();
26844                 }
26845                 this.fireEvent("afterselectionchange", this);
26846             },
26847             scope: this
26848         });
26849         this.grid.store.on('load', function(){
26850             this.selections.clear();
26851         },this);
26852         /*
26853         var view = this.grid.view;
26854         view.on("refresh", this.onRefresh, this);
26855         view.on("rowupdated", this.onRowUpdated, this);
26856         view.on("rowremoved", this.onRemove, this);
26857         */
26858     },
26859
26860     // private
26861     onRefresh : function()
26862     {
26863         var ds = this.grid.store, i, v = this.grid.view;
26864         var s = this.selections;
26865         s.each(function(r){
26866             if((i = ds.indexOfId(r.id)) != -1){
26867                 v.onRowSelect(i);
26868             }else{
26869                 s.remove(r);
26870             }
26871         });
26872     },
26873
26874     // private
26875     onRemove : function(v, index, r){
26876         this.selections.remove(r);
26877     },
26878
26879     // private
26880     onRowUpdated : function(v, index, r){
26881         if(this.isSelected(r)){
26882             v.onRowSelect(index);
26883         }
26884     },
26885
26886     /**
26887      * Select records.
26888      * @param {Array} records The records to select
26889      * @param {Boolean} keepExisting (optional) True to keep existing selections
26890      */
26891     selectRecords : function(records, keepExisting)
26892     {
26893         if(!keepExisting){
26894             this.clearSelections();
26895         }
26896             var ds = this.grid.store;
26897         for(var i = 0, len = records.length; i < len; i++){
26898             this.selectRow(ds.indexOf(records[i]), true);
26899         }
26900     },
26901
26902     /**
26903      * Gets the number of selected rows.
26904      * @return {Number}
26905      */
26906     getCount : function(){
26907         return this.selections.length;
26908     },
26909
26910     /**
26911      * Selects the first row in the grid.
26912      */
26913     selectFirstRow : function(){
26914         this.selectRow(0);
26915     },
26916
26917     /**
26918      * Select the last row.
26919      * @param {Boolean} keepExisting (optional) True to keep existing selections
26920      */
26921     selectLastRow : function(keepExisting){
26922         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26923         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26924     },
26925
26926     /**
26927      * Selects the row immediately following the last selected row.
26928      * @param {Boolean} keepExisting (optional) True to keep existing selections
26929      */
26930     selectNext : function(keepExisting)
26931     {
26932             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26933             this.selectRow(this.last+1, keepExisting);
26934             this.grid.getView().focusRow(this.last);
26935         }
26936     },
26937
26938     /**
26939      * Selects the row that precedes the last selected row.
26940      * @param {Boolean} keepExisting (optional) True to keep existing selections
26941      */
26942     selectPrevious : function(keepExisting){
26943         if(this.last){
26944             this.selectRow(this.last-1, keepExisting);
26945             this.grid.getView().focusRow(this.last);
26946         }
26947     },
26948
26949     /**
26950      * Returns the selected records
26951      * @return {Array} Array of selected records
26952      */
26953     getSelections : function(){
26954         return [].concat(this.selections.items);
26955     },
26956
26957     /**
26958      * Returns the first selected record.
26959      * @return {Record}
26960      */
26961     getSelected : function(){
26962         return this.selections.itemAt(0);
26963     },
26964
26965
26966     /**
26967      * Clears all selections.
26968      */
26969     clearSelections : function(fast)
26970     {
26971         if(this.locked) {
26972             return;
26973         }
26974         if(fast !== true){
26975                 var ds = this.grid.store;
26976             var s = this.selections;
26977             s.each(function(r){
26978                 this.deselectRow(ds.indexOfId(r.id));
26979             }, this);
26980             s.clear();
26981         }else{
26982             this.selections.clear();
26983         }
26984         this.last = false;
26985     },
26986
26987
26988     /**
26989      * Selects all rows.
26990      */
26991     selectAll : function(){
26992         if(this.locked) {
26993             return;
26994         }
26995         this.selections.clear();
26996         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26997             this.selectRow(i, true);
26998         }
26999     },
27000
27001     /**
27002      * Returns True if there is a selection.
27003      * @return {Boolean}
27004      */
27005     hasSelection : function(){
27006         return this.selections.length > 0;
27007     },
27008
27009     /**
27010      * Returns True if the specified row is selected.
27011      * @param {Number/Record} record The record or index of the record to check
27012      * @return {Boolean}
27013      */
27014     isSelected : function(index){
27015             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
27016         return (r && this.selections.key(r.id) ? true : false);
27017     },
27018
27019     /**
27020      * Returns True if the specified record id is selected.
27021      * @param {String} id The id of record to check
27022      * @return {Boolean}
27023      */
27024     isIdSelected : function(id){
27025         return (this.selections.key(id) ? true : false);
27026     },
27027
27028
27029     // private
27030     handleMouseDBClick : function(e, t){
27031         
27032     },
27033     // private
27034     handleMouseDown : function(e, t)
27035     {
27036             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
27037         if(this.isLocked() || rowIndex < 0 ){
27038             return;
27039         };
27040         if(e.shiftKey && this.last !== false){
27041             var last = this.last;
27042             this.selectRange(last, rowIndex, e.ctrlKey);
27043             this.last = last; // reset the last
27044             t.focus();
27045     
27046         }else{
27047             var isSelected = this.isSelected(rowIndex);
27048             //Roo.log("select row:" + rowIndex);
27049             if(isSelected){
27050                 this.deselectRow(rowIndex);
27051             } else {
27052                         this.selectRow(rowIndex, true);
27053             }
27054     
27055             /*
27056                 if(e.button !== 0 && isSelected){
27057                 alert('rowIndex 2: ' + rowIndex);
27058                     view.focusRow(rowIndex);
27059                 }else if(e.ctrlKey && isSelected){
27060                     this.deselectRow(rowIndex);
27061                 }else if(!isSelected){
27062                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27063                     view.focusRow(rowIndex);
27064                 }
27065             */
27066         }
27067         this.fireEvent("afterselectionchange", this);
27068     },
27069     // private
27070     handleDragableRowClick :  function(grid, rowIndex, e) 
27071     {
27072         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27073             this.selectRow(rowIndex, false);
27074             grid.view.focusRow(rowIndex);
27075              this.fireEvent("afterselectionchange", this);
27076         }
27077     },
27078     
27079     /**
27080      * Selects multiple rows.
27081      * @param {Array} rows Array of the indexes of the row to select
27082      * @param {Boolean} keepExisting (optional) True to keep existing selections
27083      */
27084     selectRows : function(rows, keepExisting){
27085         if(!keepExisting){
27086             this.clearSelections();
27087         }
27088         for(var i = 0, len = rows.length; i < len; i++){
27089             this.selectRow(rows[i], true);
27090         }
27091     },
27092
27093     /**
27094      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27095      * @param {Number} startRow The index of the first row in the range
27096      * @param {Number} endRow The index of the last row in the range
27097      * @param {Boolean} keepExisting (optional) True to retain existing selections
27098      */
27099     selectRange : function(startRow, endRow, keepExisting){
27100         if(this.locked) {
27101             return;
27102         }
27103         if(!keepExisting){
27104             this.clearSelections();
27105         }
27106         if(startRow <= endRow){
27107             for(var i = startRow; i <= endRow; i++){
27108                 this.selectRow(i, true);
27109             }
27110         }else{
27111             for(var i = startRow; i >= endRow; i--){
27112                 this.selectRow(i, true);
27113             }
27114         }
27115     },
27116
27117     /**
27118      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27119      * @param {Number} startRow The index of the first row in the range
27120      * @param {Number} endRow The index of the last row in the range
27121      */
27122     deselectRange : function(startRow, endRow, preventViewNotify){
27123         if(this.locked) {
27124             return;
27125         }
27126         for(var i = startRow; i <= endRow; i++){
27127             this.deselectRow(i, preventViewNotify);
27128         }
27129     },
27130
27131     /**
27132      * Selects a row.
27133      * @param {Number} row The index of the row to select
27134      * @param {Boolean} keepExisting (optional) True to keep existing selections
27135      */
27136     selectRow : function(index, keepExisting, preventViewNotify)
27137     {
27138             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27139             return;
27140         }
27141         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27142             if(!keepExisting || this.singleSelect){
27143                 this.clearSelections();
27144             }
27145             
27146             var r = this.grid.store.getAt(index);
27147             //console.log('selectRow - record id :' + r.id);
27148             
27149             this.selections.add(r);
27150             this.last = this.lastActive = index;
27151             if(!preventViewNotify){
27152                 var proxy = new Roo.Element(
27153                                 this.grid.getRowDom(index)
27154                 );
27155                 proxy.addClass('bg-info info');
27156             }
27157             this.fireEvent("rowselect", this, index, r);
27158             this.fireEvent("selectionchange", this);
27159         }
27160     },
27161
27162     /**
27163      * Deselects a row.
27164      * @param {Number} row The index of the row to deselect
27165      */
27166     deselectRow : function(index, preventViewNotify)
27167     {
27168         if(this.locked) {
27169             return;
27170         }
27171         if(this.last == index){
27172             this.last = false;
27173         }
27174         if(this.lastActive == index){
27175             this.lastActive = false;
27176         }
27177         
27178         var r = this.grid.store.getAt(index);
27179         if (!r) {
27180             return;
27181         }
27182         
27183         this.selections.remove(r);
27184         //.console.log('deselectRow - record id :' + r.id);
27185         if(!preventViewNotify){
27186         
27187             var proxy = new Roo.Element(
27188                 this.grid.getRowDom(index)
27189             );
27190             proxy.removeClass('bg-info info');
27191         }
27192         this.fireEvent("rowdeselect", this, index);
27193         this.fireEvent("selectionchange", this);
27194     },
27195
27196     // private
27197     restoreLast : function(){
27198         if(this._last){
27199             this.last = this._last;
27200         }
27201     },
27202
27203     // private
27204     acceptsNav : function(row, col, cm){
27205         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27206     },
27207
27208     // private
27209     onEditorKey : function(field, e){
27210         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27211         if(k == e.TAB){
27212             e.stopEvent();
27213             ed.completeEdit();
27214             if(e.shiftKey){
27215                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27216             }else{
27217                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27218             }
27219         }else if(k == e.ENTER && !e.ctrlKey){
27220             e.stopEvent();
27221             ed.completeEdit();
27222             if(e.shiftKey){
27223                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27224             }else{
27225                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27226             }
27227         }else if(k == e.ESC){
27228             ed.cancelEdit();
27229         }
27230         if(newCell){
27231             g.startEditing(newCell[0], newCell[1]);
27232         }
27233     }
27234 });
27235 /*
27236  * Based on:
27237  * Ext JS Library 1.1.1
27238  * Copyright(c) 2006-2007, Ext JS, LLC.
27239  *
27240  * Originally Released Under LGPL - original licence link has changed is not relivant.
27241  *
27242  * Fork - LGPL
27243  * <script type="text/javascript">
27244  */
27245  
27246 /**
27247  * @class Roo.bootstrap.PagingToolbar
27248  * @extends Roo.bootstrap.NavSimplebar
27249  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27250  * @constructor
27251  * Create a new PagingToolbar
27252  * @param {Object} config The config object
27253  * @param {Roo.data.Store} store
27254  */
27255 Roo.bootstrap.PagingToolbar = function(config)
27256 {
27257     // old args format still supported... - xtype is prefered..
27258         // created from xtype...
27259     
27260     this.ds = config.dataSource;
27261     
27262     if (config.store && !this.ds) {
27263         this.store= Roo.factory(config.store, Roo.data);
27264         this.ds = this.store;
27265         this.ds.xmodule = this.xmodule || false;
27266     }
27267     
27268     this.toolbarItems = [];
27269     if (config.items) {
27270         this.toolbarItems = config.items;
27271     }
27272     
27273     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27274     
27275     this.cursor = 0;
27276     
27277     if (this.ds) { 
27278         this.bind(this.ds);
27279     }
27280     
27281     if (Roo.bootstrap.version == 4) {
27282         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27283     } else {
27284         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27285     }
27286     
27287 };
27288
27289 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27290     /**
27291      * @cfg {Roo.data.Store} dataSource
27292      * The underlying data store providing the paged data
27293      */
27294     /**
27295      * @cfg {String/HTMLElement/Element} container
27296      * container The id or element that will contain the toolbar
27297      */
27298     /**
27299      * @cfg {Boolean} displayInfo
27300      * True to display the displayMsg (defaults to false)
27301      */
27302     /**
27303      * @cfg {Number} pageSize
27304      * The number of records to display per page (defaults to 20)
27305      */
27306     pageSize: 20,
27307     /**
27308      * @cfg {String} displayMsg
27309      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27310      */
27311     displayMsg : 'Displaying {0} - {1} of {2}',
27312     /**
27313      * @cfg {String} emptyMsg
27314      * The message to display when no records are found (defaults to "No data to display")
27315      */
27316     emptyMsg : 'No data to display',
27317     /**
27318      * Customizable piece of the default paging text (defaults to "Page")
27319      * @type String
27320      */
27321     beforePageText : "Page",
27322     /**
27323      * Customizable piece of the default paging text (defaults to "of %0")
27324      * @type String
27325      */
27326     afterPageText : "of {0}",
27327     /**
27328      * Customizable piece of the default paging text (defaults to "First Page")
27329      * @type String
27330      */
27331     firstText : "First Page",
27332     /**
27333      * Customizable piece of the default paging text (defaults to "Previous Page")
27334      * @type String
27335      */
27336     prevText : "Previous Page",
27337     /**
27338      * Customizable piece of the default paging text (defaults to "Next Page")
27339      * @type String
27340      */
27341     nextText : "Next Page",
27342     /**
27343      * Customizable piece of the default paging text (defaults to "Last Page")
27344      * @type String
27345      */
27346     lastText : "Last Page",
27347     /**
27348      * Customizable piece of the default paging text (defaults to "Refresh")
27349      * @type String
27350      */
27351     refreshText : "Refresh",
27352
27353     buttons : false,
27354     // private
27355     onRender : function(ct, position) 
27356     {
27357         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27358         this.navgroup.parentId = this.id;
27359         this.navgroup.onRender(this.el, null);
27360         // add the buttons to the navgroup
27361         
27362         if(this.displayInfo){
27363             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27364             this.displayEl = this.el.select('.x-paging-info', true).first();
27365 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27366 //            this.displayEl = navel.el.select('span',true).first();
27367         }
27368         
27369         var _this = this;
27370         
27371         if(this.buttons){
27372             Roo.each(_this.buttons, function(e){ // this might need to use render????
27373                Roo.factory(e).render(_this.el);
27374             });
27375         }
27376             
27377         Roo.each(_this.toolbarItems, function(e) {
27378             _this.navgroup.addItem(e);
27379         });
27380         
27381         
27382         this.first = this.navgroup.addItem({
27383             tooltip: this.firstText,
27384             cls: "prev btn-outline-secondary",
27385             html : ' <i class="fa fa-step-backward"></i>',
27386             disabled: true,
27387             preventDefault: true,
27388             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27389         });
27390         
27391         this.prev =  this.navgroup.addItem({
27392             tooltip: this.prevText,
27393             cls: "prev btn-outline-secondary",
27394             html : ' <i class="fa fa-backward"></i>',
27395             disabled: true,
27396             preventDefault: true,
27397             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27398         });
27399     //this.addSeparator();
27400         
27401         
27402         var field = this.navgroup.addItem( {
27403             tagtype : 'span',
27404             cls : 'x-paging-position  btn-outline-secondary',
27405              disabled: true,
27406             html : this.beforePageText  +
27407                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27408                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27409          } ); //?? escaped?
27410         
27411         this.field = field.el.select('input', true).first();
27412         this.field.on("keydown", this.onPagingKeydown, this);
27413         this.field.on("focus", function(){this.dom.select();});
27414     
27415     
27416         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27417         //this.field.setHeight(18);
27418         //this.addSeparator();
27419         this.next = this.navgroup.addItem({
27420             tooltip: this.nextText,
27421             cls: "next btn-outline-secondary",
27422             html : ' <i class="fa fa-forward"></i>',
27423             disabled: true,
27424             preventDefault: true,
27425             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27426         });
27427         this.last = this.navgroup.addItem({
27428             tooltip: this.lastText,
27429             html : ' <i class="fa fa-step-forward"></i>',
27430             cls: "next btn-outline-secondary",
27431             disabled: true,
27432             preventDefault: true,
27433             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27434         });
27435     //this.addSeparator();
27436         this.loading = this.navgroup.addItem({
27437             tooltip: this.refreshText,
27438             cls: "btn-outline-secondary",
27439             html : ' <i class="fa fa-refresh"></i>',
27440             preventDefault: true,
27441             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27442         });
27443         
27444     },
27445
27446     // private
27447     updateInfo : function(){
27448         if(this.displayEl){
27449             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27450             var msg = count == 0 ?
27451                 this.emptyMsg :
27452                 String.format(
27453                     this.displayMsg,
27454                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27455                 );
27456             this.displayEl.update(msg);
27457         }
27458     },
27459
27460     // private
27461     onLoad : function(ds, r, o)
27462     {
27463         this.cursor = o.params && o.params.start ? o.params.start : 0;
27464         
27465         var d = this.getPageData(),
27466             ap = d.activePage,
27467             ps = d.pages;
27468         
27469         
27470         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27471         this.field.dom.value = ap;
27472         this.first.setDisabled(ap == 1);
27473         this.prev.setDisabled(ap == 1);
27474         this.next.setDisabled(ap == ps);
27475         this.last.setDisabled(ap == ps);
27476         this.loading.enable();
27477         this.updateInfo();
27478     },
27479
27480     // private
27481     getPageData : function(){
27482         var total = this.ds.getTotalCount();
27483         return {
27484             total : total,
27485             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27486             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27487         };
27488     },
27489
27490     // private
27491     onLoadError : function(){
27492         this.loading.enable();
27493     },
27494
27495     // private
27496     onPagingKeydown : function(e){
27497         var k = e.getKey();
27498         var d = this.getPageData();
27499         if(k == e.RETURN){
27500             var v = this.field.dom.value, pageNum;
27501             if(!v || isNaN(pageNum = parseInt(v, 10))){
27502                 this.field.dom.value = d.activePage;
27503                 return;
27504             }
27505             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27506             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27507             e.stopEvent();
27508         }
27509         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))
27510         {
27511           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27512           this.field.dom.value = pageNum;
27513           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27514           e.stopEvent();
27515         }
27516         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27517         {
27518           var v = this.field.dom.value, pageNum; 
27519           var increment = (e.shiftKey) ? 10 : 1;
27520           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27521                 increment *= -1;
27522           }
27523           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27524             this.field.dom.value = d.activePage;
27525             return;
27526           }
27527           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27528           {
27529             this.field.dom.value = parseInt(v, 10) + increment;
27530             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27531             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27532           }
27533           e.stopEvent();
27534         }
27535     },
27536
27537     // private
27538     beforeLoad : function(){
27539         if(this.loading){
27540             this.loading.disable();
27541         }
27542     },
27543
27544     // private
27545     onClick : function(which){
27546         
27547         var ds = this.ds;
27548         if (!ds) {
27549             return;
27550         }
27551         
27552         switch(which){
27553             case "first":
27554                 ds.load({params:{start: 0, limit: this.pageSize}});
27555             break;
27556             case "prev":
27557                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27558             break;
27559             case "next":
27560                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27561             break;
27562             case "last":
27563                 var total = ds.getTotalCount();
27564                 var extra = total % this.pageSize;
27565                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27566                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27567             break;
27568             case "refresh":
27569                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27570             break;
27571         }
27572     },
27573
27574     /**
27575      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27576      * @param {Roo.data.Store} store The data store to unbind
27577      */
27578     unbind : function(ds){
27579         ds.un("beforeload", this.beforeLoad, this);
27580         ds.un("load", this.onLoad, this);
27581         ds.un("loadexception", this.onLoadError, this);
27582         ds.un("remove", this.updateInfo, this);
27583         ds.un("add", this.updateInfo, this);
27584         this.ds = undefined;
27585     },
27586
27587     /**
27588      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27589      * @param {Roo.data.Store} store The data store to bind
27590      */
27591     bind : function(ds){
27592         ds.on("beforeload", this.beforeLoad, this);
27593         ds.on("load", this.onLoad, this);
27594         ds.on("loadexception", this.onLoadError, this);
27595         ds.on("remove", this.updateInfo, this);
27596         ds.on("add", this.updateInfo, this);
27597         this.ds = ds;
27598     }
27599 });/*
27600  * - LGPL
27601  *
27602  * element
27603  * 
27604  */
27605
27606 /**
27607  * @class Roo.bootstrap.MessageBar
27608  * @extends Roo.bootstrap.Component
27609  * Bootstrap MessageBar class
27610  * @cfg {String} html contents of the MessageBar
27611  * @cfg {String} weight (info | success | warning | danger) default info
27612  * @cfg {String} beforeClass insert the bar before the given class
27613  * @cfg {Boolean} closable (true | false) default false
27614  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27615  * 
27616  * @constructor
27617  * Create a new Element
27618  * @param {Object} config The config object
27619  */
27620
27621 Roo.bootstrap.MessageBar = function(config){
27622     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27623 };
27624
27625 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27626     
27627     html: '',
27628     weight: 'info',
27629     closable: false,
27630     fixed: false,
27631     beforeClass: 'bootstrap-sticky-wrap',
27632     
27633     getAutoCreate : function(){
27634         
27635         var cfg = {
27636             tag: 'div',
27637             cls: 'alert alert-dismissable alert-' + this.weight,
27638             cn: [
27639                 {
27640                     tag: 'span',
27641                     cls: 'message',
27642                     html: this.html || ''
27643                 }
27644             ]
27645         };
27646         
27647         if(this.fixed){
27648             cfg.cls += ' alert-messages-fixed';
27649         }
27650         
27651         if(this.closable){
27652             cfg.cn.push({
27653                 tag: 'button',
27654                 cls: 'close',
27655                 html: 'x'
27656             });
27657         }
27658         
27659         return cfg;
27660     },
27661     
27662     onRender : function(ct, position)
27663     {
27664         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27665         
27666         if(!this.el){
27667             var cfg = Roo.apply({},  this.getAutoCreate());
27668             cfg.id = Roo.id();
27669             
27670             if (this.cls) {
27671                 cfg.cls += ' ' + this.cls;
27672             }
27673             if (this.style) {
27674                 cfg.style = this.style;
27675             }
27676             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27677             
27678             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27679         }
27680         
27681         this.el.select('>button.close').on('click', this.hide, this);
27682         
27683     },
27684     
27685     show : function()
27686     {
27687         if (!this.rendered) {
27688             this.render();
27689         }
27690         
27691         this.el.show();
27692         
27693         this.fireEvent('show', this);
27694         
27695     },
27696     
27697     hide : function()
27698     {
27699         if (!this.rendered) {
27700             this.render();
27701         }
27702         
27703         this.el.hide();
27704         
27705         this.fireEvent('hide', this);
27706     },
27707     
27708     update : function()
27709     {
27710 //        var e = this.el.dom.firstChild;
27711 //        
27712 //        if(this.closable){
27713 //            e = e.nextSibling;
27714 //        }
27715 //        
27716 //        e.data = this.html || '';
27717
27718         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27719     }
27720    
27721 });
27722
27723  
27724
27725      /*
27726  * - LGPL
27727  *
27728  * Graph
27729  * 
27730  */
27731
27732
27733 /**
27734  * @class Roo.bootstrap.Graph
27735  * @extends Roo.bootstrap.Component
27736  * Bootstrap Graph class
27737 > Prameters
27738  -sm {number} sm 4
27739  -md {number} md 5
27740  @cfg {String} graphtype  bar | vbar | pie
27741  @cfg {number} g_x coodinator | centre x (pie)
27742  @cfg {number} g_y coodinator | centre y (pie)
27743  @cfg {number} g_r radius (pie)
27744  @cfg {number} g_height height of the chart (respected by all elements in the set)
27745  @cfg {number} g_width width of the chart (respected by all elements in the set)
27746  @cfg {Object} title The title of the chart
27747     
27748  -{Array}  values
27749  -opts (object) options for the chart 
27750      o {
27751      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27752      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27753      o vgutter (number)
27754      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.
27755      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27756      o to
27757      o stretch (boolean)
27758      o }
27759  -opts (object) options for the pie
27760      o{
27761      o cut
27762      o startAngle (number)
27763      o endAngle (number)
27764      } 
27765  *
27766  * @constructor
27767  * Create a new Input
27768  * @param {Object} config The config object
27769  */
27770
27771 Roo.bootstrap.Graph = function(config){
27772     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27773     
27774     this.addEvents({
27775         // img events
27776         /**
27777          * @event click
27778          * The img click event for the img.
27779          * @param {Roo.EventObject} e
27780          */
27781         "click" : true
27782     });
27783 };
27784
27785 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27786     
27787     sm: 4,
27788     md: 5,
27789     graphtype: 'bar',
27790     g_height: 250,
27791     g_width: 400,
27792     g_x: 50,
27793     g_y: 50,
27794     g_r: 30,
27795     opts:{
27796         //g_colors: this.colors,
27797         g_type: 'soft',
27798         g_gutter: '20%'
27799
27800     },
27801     title : false,
27802
27803     getAutoCreate : function(){
27804         
27805         var cfg = {
27806             tag: 'div',
27807             html : null
27808         };
27809         
27810         
27811         return  cfg;
27812     },
27813
27814     onRender : function(ct,position){
27815         
27816         
27817         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27818         
27819         if (typeof(Raphael) == 'undefined') {
27820             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27821             return;
27822         }
27823         
27824         this.raphael = Raphael(this.el.dom);
27825         
27826                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27827                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27828                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27829                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27830                 /*
27831                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27832                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27833                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27834                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27835                 
27836                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27837                 r.barchart(330, 10, 300, 220, data1);
27838                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27839                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27840                 */
27841                 
27842                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27843                 // r.barchart(30, 30, 560, 250,  xdata, {
27844                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27845                 //     axis : "0 0 1 1",
27846                 //     axisxlabels :  xdata
27847                 //     //yvalues : cols,
27848                    
27849                 // });
27850 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27851 //        
27852 //        this.load(null,xdata,{
27853 //                axis : "0 0 1 1",
27854 //                axisxlabels :  xdata
27855 //                });
27856
27857     },
27858
27859     load : function(graphtype,xdata,opts)
27860     {
27861         this.raphael.clear();
27862         if(!graphtype) {
27863             graphtype = this.graphtype;
27864         }
27865         if(!opts){
27866             opts = this.opts;
27867         }
27868         var r = this.raphael,
27869             fin = function () {
27870                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27871             },
27872             fout = function () {
27873                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27874             },
27875             pfin = function() {
27876                 this.sector.stop();
27877                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27878
27879                 if (this.label) {
27880                     this.label[0].stop();
27881                     this.label[0].attr({ r: 7.5 });
27882                     this.label[1].attr({ "font-weight": 800 });
27883                 }
27884             },
27885             pfout = function() {
27886                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27887
27888                 if (this.label) {
27889                     this.label[0].animate({ r: 5 }, 500, "bounce");
27890                     this.label[1].attr({ "font-weight": 400 });
27891                 }
27892             };
27893
27894         switch(graphtype){
27895             case 'bar':
27896                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27897                 break;
27898             case 'hbar':
27899                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27900                 break;
27901             case 'pie':
27902 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27903 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27904 //            
27905                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27906                 
27907                 break;
27908
27909         }
27910         
27911         if(this.title){
27912             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27913         }
27914         
27915     },
27916     
27917     setTitle: function(o)
27918     {
27919         this.title = o;
27920     },
27921     
27922     initEvents: function() {
27923         
27924         if(!this.href){
27925             this.el.on('click', this.onClick, this);
27926         }
27927     },
27928     
27929     onClick : function(e)
27930     {
27931         Roo.log('img onclick');
27932         this.fireEvent('click', this, e);
27933     }
27934    
27935 });
27936
27937  
27938 /*
27939  * - LGPL
27940  *
27941  * numberBox
27942  * 
27943  */
27944 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27945
27946 /**
27947  * @class Roo.bootstrap.dash.NumberBox
27948  * @extends Roo.bootstrap.Component
27949  * Bootstrap NumberBox class
27950  * @cfg {String} headline Box headline
27951  * @cfg {String} content Box content
27952  * @cfg {String} icon Box icon
27953  * @cfg {String} footer Footer text
27954  * @cfg {String} fhref Footer href
27955  * 
27956  * @constructor
27957  * Create a new NumberBox
27958  * @param {Object} config The config object
27959  */
27960
27961
27962 Roo.bootstrap.dash.NumberBox = function(config){
27963     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27964     
27965 };
27966
27967 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27968     
27969     headline : '',
27970     content : '',
27971     icon : '',
27972     footer : '',
27973     fhref : '',
27974     ficon : '',
27975     
27976     getAutoCreate : function(){
27977         
27978         var cfg = {
27979             tag : 'div',
27980             cls : 'small-box ',
27981             cn : [
27982                 {
27983                     tag : 'div',
27984                     cls : 'inner',
27985                     cn :[
27986                         {
27987                             tag : 'h3',
27988                             cls : 'roo-headline',
27989                             html : this.headline
27990                         },
27991                         {
27992                             tag : 'p',
27993                             cls : 'roo-content',
27994                             html : this.content
27995                         }
27996                     ]
27997                 }
27998             ]
27999         };
28000         
28001         if(this.icon){
28002             cfg.cn.push({
28003                 tag : 'div',
28004                 cls : 'icon',
28005                 cn :[
28006                     {
28007                         tag : 'i',
28008                         cls : 'ion ' + this.icon
28009                     }
28010                 ]
28011             });
28012         }
28013         
28014         if(this.footer){
28015             var footer = {
28016                 tag : 'a',
28017                 cls : 'small-box-footer',
28018                 href : this.fhref || '#',
28019                 html : this.footer
28020             };
28021             
28022             cfg.cn.push(footer);
28023             
28024         }
28025         
28026         return  cfg;
28027     },
28028
28029     onRender : function(ct,position){
28030         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
28031
28032
28033        
28034                 
28035     },
28036
28037     setHeadline: function (value)
28038     {
28039         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
28040     },
28041     
28042     setFooter: function (value, href)
28043     {
28044         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
28045         
28046         if(href){
28047             this.el.select('a.small-box-footer',true).first().attr('href', href);
28048         }
28049         
28050     },
28051
28052     setContent: function (value)
28053     {
28054         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28055     },
28056
28057     initEvents: function() 
28058     {   
28059         
28060     }
28061     
28062 });
28063
28064  
28065 /*
28066  * - LGPL
28067  *
28068  * TabBox
28069  * 
28070  */
28071 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28072
28073 /**
28074  * @class Roo.bootstrap.dash.TabBox
28075  * @extends Roo.bootstrap.Component
28076  * Bootstrap TabBox class
28077  * @cfg {String} title Title of the TabBox
28078  * @cfg {String} icon Icon of the TabBox
28079  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28080  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28081  * 
28082  * @constructor
28083  * Create a new TabBox
28084  * @param {Object} config The config object
28085  */
28086
28087
28088 Roo.bootstrap.dash.TabBox = function(config){
28089     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28090     this.addEvents({
28091         // raw events
28092         /**
28093          * @event addpane
28094          * When a pane is added
28095          * @param {Roo.bootstrap.dash.TabPane} pane
28096          */
28097         "addpane" : true,
28098         /**
28099          * @event activatepane
28100          * When a pane is activated
28101          * @param {Roo.bootstrap.dash.TabPane} pane
28102          */
28103         "activatepane" : true
28104         
28105          
28106     });
28107     
28108     this.panes = [];
28109 };
28110
28111 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28112
28113     title : '',
28114     icon : false,
28115     showtabs : true,
28116     tabScrollable : false,
28117     
28118     getChildContainer : function()
28119     {
28120         return this.el.select('.tab-content', true).first();
28121     },
28122     
28123     getAutoCreate : function(){
28124         
28125         var header = {
28126             tag: 'li',
28127             cls: 'pull-left header',
28128             html: this.title,
28129             cn : []
28130         };
28131         
28132         if(this.icon){
28133             header.cn.push({
28134                 tag: 'i',
28135                 cls: 'fa ' + this.icon
28136             });
28137         }
28138         
28139         var h = {
28140             tag: 'ul',
28141             cls: 'nav nav-tabs pull-right',
28142             cn: [
28143                 header
28144             ]
28145         };
28146         
28147         if(this.tabScrollable){
28148             h = {
28149                 tag: 'div',
28150                 cls: 'tab-header',
28151                 cn: [
28152                     {
28153                         tag: 'ul',
28154                         cls: 'nav nav-tabs pull-right',
28155                         cn: [
28156                             header
28157                         ]
28158                     }
28159                 ]
28160             };
28161         }
28162         
28163         var cfg = {
28164             tag: 'div',
28165             cls: 'nav-tabs-custom',
28166             cn: [
28167                 h,
28168                 {
28169                     tag: 'div',
28170                     cls: 'tab-content no-padding',
28171                     cn: []
28172                 }
28173             ]
28174         };
28175
28176         return  cfg;
28177     },
28178     initEvents : function()
28179     {
28180         //Roo.log('add add pane handler');
28181         this.on('addpane', this.onAddPane, this);
28182     },
28183      /**
28184      * Updates the box title
28185      * @param {String} html to set the title to.
28186      */
28187     setTitle : function(value)
28188     {
28189         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28190     },
28191     onAddPane : function(pane)
28192     {
28193         this.panes.push(pane);
28194         //Roo.log('addpane');
28195         //Roo.log(pane);
28196         // tabs are rendere left to right..
28197         if(!this.showtabs){
28198             return;
28199         }
28200         
28201         var ctr = this.el.select('.nav-tabs', true).first();
28202          
28203          
28204         var existing = ctr.select('.nav-tab',true);
28205         var qty = existing.getCount();;
28206         
28207         
28208         var tab = ctr.createChild({
28209             tag : 'li',
28210             cls : 'nav-tab' + (qty ? '' : ' active'),
28211             cn : [
28212                 {
28213                     tag : 'a',
28214                     href:'#',
28215                     html : pane.title
28216                 }
28217             ]
28218         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28219         pane.tab = tab;
28220         
28221         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28222         if (!qty) {
28223             pane.el.addClass('active');
28224         }
28225         
28226                 
28227     },
28228     onTabClick : function(ev,un,ob,pane)
28229     {
28230         //Roo.log('tab - prev default');
28231         ev.preventDefault();
28232         
28233         
28234         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28235         pane.tab.addClass('active');
28236         //Roo.log(pane.title);
28237         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28238         // technically we should have a deactivate event.. but maybe add later.
28239         // and it should not de-activate the selected tab...
28240         this.fireEvent('activatepane', pane);
28241         pane.el.addClass('active');
28242         pane.fireEvent('activate');
28243         
28244         
28245     },
28246     
28247     getActivePane : function()
28248     {
28249         var r = false;
28250         Roo.each(this.panes, function(p) {
28251             if(p.el.hasClass('active')){
28252                 r = p;
28253                 return false;
28254             }
28255             
28256             return;
28257         });
28258         
28259         return r;
28260     }
28261     
28262     
28263 });
28264
28265  
28266 /*
28267  * - LGPL
28268  *
28269  * Tab pane
28270  * 
28271  */
28272 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28273 /**
28274  * @class Roo.bootstrap.TabPane
28275  * @extends Roo.bootstrap.Component
28276  * Bootstrap TabPane class
28277  * @cfg {Boolean} active (false | true) Default false
28278  * @cfg {String} title title of panel
28279
28280  * 
28281  * @constructor
28282  * Create a new TabPane
28283  * @param {Object} config The config object
28284  */
28285
28286 Roo.bootstrap.dash.TabPane = function(config){
28287     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28288     
28289     this.addEvents({
28290         // raw events
28291         /**
28292          * @event activate
28293          * When a pane is activated
28294          * @param {Roo.bootstrap.dash.TabPane} pane
28295          */
28296         "activate" : true
28297          
28298     });
28299 };
28300
28301 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28302     
28303     active : false,
28304     title : '',
28305     
28306     // the tabBox that this is attached to.
28307     tab : false,
28308      
28309     getAutoCreate : function() 
28310     {
28311         var cfg = {
28312             tag: 'div',
28313             cls: 'tab-pane'
28314         };
28315         
28316         if(this.active){
28317             cfg.cls += ' active';
28318         }
28319         
28320         return cfg;
28321     },
28322     initEvents  : function()
28323     {
28324         //Roo.log('trigger add pane handler');
28325         this.parent().fireEvent('addpane', this)
28326     },
28327     
28328      /**
28329      * Updates the tab title 
28330      * @param {String} html to set the title to.
28331      */
28332     setTitle: function(str)
28333     {
28334         if (!this.tab) {
28335             return;
28336         }
28337         this.title = str;
28338         this.tab.select('a', true).first().dom.innerHTML = str;
28339         
28340     }
28341     
28342     
28343     
28344 });
28345
28346  
28347
28348
28349  /*
28350  * - LGPL
28351  *
28352  * menu
28353  * 
28354  */
28355 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28356
28357 /**
28358  * @class Roo.bootstrap.menu.Menu
28359  * @extends Roo.bootstrap.Component
28360  * Bootstrap Menu class - container for Menu
28361  * @cfg {String} html Text of the menu
28362  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28363  * @cfg {String} icon Font awesome icon
28364  * @cfg {String} pos Menu align to (top | bottom) default bottom
28365  * 
28366  * 
28367  * @constructor
28368  * Create a new Menu
28369  * @param {Object} config The config object
28370  */
28371
28372
28373 Roo.bootstrap.menu.Menu = function(config){
28374     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28375     
28376     this.addEvents({
28377         /**
28378          * @event beforeshow
28379          * Fires before this menu is displayed
28380          * @param {Roo.bootstrap.menu.Menu} this
28381          */
28382         beforeshow : true,
28383         /**
28384          * @event beforehide
28385          * Fires before this menu is hidden
28386          * @param {Roo.bootstrap.menu.Menu} this
28387          */
28388         beforehide : true,
28389         /**
28390          * @event show
28391          * Fires after this menu is displayed
28392          * @param {Roo.bootstrap.menu.Menu} this
28393          */
28394         show : true,
28395         /**
28396          * @event hide
28397          * Fires after this menu is hidden
28398          * @param {Roo.bootstrap.menu.Menu} this
28399          */
28400         hide : true,
28401         /**
28402          * @event click
28403          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28404          * @param {Roo.bootstrap.menu.Menu} this
28405          * @param {Roo.EventObject} e
28406          */
28407         click : true
28408     });
28409     
28410 };
28411
28412 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28413     
28414     submenu : false,
28415     html : '',
28416     weight : 'default',
28417     icon : false,
28418     pos : 'bottom',
28419     
28420     
28421     getChildContainer : function() {
28422         if(this.isSubMenu){
28423             return this.el;
28424         }
28425         
28426         return this.el.select('ul.dropdown-menu', true).first();  
28427     },
28428     
28429     getAutoCreate : function()
28430     {
28431         var text = [
28432             {
28433                 tag : 'span',
28434                 cls : 'roo-menu-text',
28435                 html : this.html
28436             }
28437         ];
28438         
28439         if(this.icon){
28440             text.unshift({
28441                 tag : 'i',
28442                 cls : 'fa ' + this.icon
28443             })
28444         }
28445         
28446         
28447         var cfg = {
28448             tag : 'div',
28449             cls : 'btn-group',
28450             cn : [
28451                 {
28452                     tag : 'button',
28453                     cls : 'dropdown-button btn btn-' + this.weight,
28454                     cn : text
28455                 },
28456                 {
28457                     tag : 'button',
28458                     cls : 'dropdown-toggle btn btn-' + this.weight,
28459                     cn : [
28460                         {
28461                             tag : 'span',
28462                             cls : 'caret'
28463                         }
28464                     ]
28465                 },
28466                 {
28467                     tag : 'ul',
28468                     cls : 'dropdown-menu'
28469                 }
28470             ]
28471             
28472         };
28473         
28474         if(this.pos == 'top'){
28475             cfg.cls += ' dropup';
28476         }
28477         
28478         if(this.isSubMenu){
28479             cfg = {
28480                 tag : 'ul',
28481                 cls : 'dropdown-menu'
28482             }
28483         }
28484         
28485         return cfg;
28486     },
28487     
28488     onRender : function(ct, position)
28489     {
28490         this.isSubMenu = ct.hasClass('dropdown-submenu');
28491         
28492         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28493     },
28494     
28495     initEvents : function() 
28496     {
28497         if(this.isSubMenu){
28498             return;
28499         }
28500         
28501         this.hidden = true;
28502         
28503         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28504         this.triggerEl.on('click', this.onTriggerPress, this);
28505         
28506         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28507         this.buttonEl.on('click', this.onClick, this);
28508         
28509     },
28510     
28511     list : function()
28512     {
28513         if(this.isSubMenu){
28514             return this.el;
28515         }
28516         
28517         return this.el.select('ul.dropdown-menu', true).first();
28518     },
28519     
28520     onClick : function(e)
28521     {
28522         this.fireEvent("click", this, e);
28523     },
28524     
28525     onTriggerPress  : function(e)
28526     {   
28527         if (this.isVisible()) {
28528             this.hide();
28529         } else {
28530             this.show();
28531         }
28532     },
28533     
28534     isVisible : function(){
28535         return !this.hidden;
28536     },
28537     
28538     show : function()
28539     {
28540         this.fireEvent("beforeshow", this);
28541         
28542         this.hidden = false;
28543         this.el.addClass('open');
28544         
28545         Roo.get(document).on("mouseup", this.onMouseUp, this);
28546         
28547         this.fireEvent("show", this);
28548         
28549         
28550     },
28551     
28552     hide : function()
28553     {
28554         this.fireEvent("beforehide", this);
28555         
28556         this.hidden = true;
28557         this.el.removeClass('open');
28558         
28559         Roo.get(document).un("mouseup", this.onMouseUp);
28560         
28561         this.fireEvent("hide", this);
28562     },
28563     
28564     onMouseUp : function()
28565     {
28566         this.hide();
28567     }
28568     
28569 });
28570
28571  
28572  /*
28573  * - LGPL
28574  *
28575  * menu item
28576  * 
28577  */
28578 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28579
28580 /**
28581  * @class Roo.bootstrap.menu.Item
28582  * @extends Roo.bootstrap.Component
28583  * Bootstrap MenuItem class
28584  * @cfg {Boolean} submenu (true | false) default false
28585  * @cfg {String} html text of the item
28586  * @cfg {String} href the link
28587  * @cfg {Boolean} disable (true | false) default false
28588  * @cfg {Boolean} preventDefault (true | false) default true
28589  * @cfg {String} icon Font awesome icon
28590  * @cfg {String} pos Submenu align to (left | right) default right 
28591  * 
28592  * 
28593  * @constructor
28594  * Create a new Item
28595  * @param {Object} config The config object
28596  */
28597
28598
28599 Roo.bootstrap.menu.Item = function(config){
28600     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28601     this.addEvents({
28602         /**
28603          * @event mouseover
28604          * Fires when the mouse is hovering over this menu
28605          * @param {Roo.bootstrap.menu.Item} this
28606          * @param {Roo.EventObject} e
28607          */
28608         mouseover : true,
28609         /**
28610          * @event mouseout
28611          * Fires when the mouse exits this menu
28612          * @param {Roo.bootstrap.menu.Item} this
28613          * @param {Roo.EventObject} e
28614          */
28615         mouseout : true,
28616         // raw events
28617         /**
28618          * @event click
28619          * The raw click event for the entire grid.
28620          * @param {Roo.EventObject} e
28621          */
28622         click : true
28623     });
28624 };
28625
28626 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28627     
28628     submenu : false,
28629     href : '',
28630     html : '',
28631     preventDefault: true,
28632     disable : false,
28633     icon : false,
28634     pos : 'right',
28635     
28636     getAutoCreate : function()
28637     {
28638         var text = [
28639             {
28640                 tag : 'span',
28641                 cls : 'roo-menu-item-text',
28642                 html : this.html
28643             }
28644         ];
28645         
28646         if(this.icon){
28647             text.unshift({
28648                 tag : 'i',
28649                 cls : 'fa ' + this.icon
28650             })
28651         }
28652         
28653         var cfg = {
28654             tag : 'li',
28655             cn : [
28656                 {
28657                     tag : 'a',
28658                     href : this.href || '#',
28659                     cn : text
28660                 }
28661             ]
28662         };
28663         
28664         if(this.disable){
28665             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28666         }
28667         
28668         if(this.submenu){
28669             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28670             
28671             if(this.pos == 'left'){
28672                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28673             }
28674         }
28675         
28676         return cfg;
28677     },
28678     
28679     initEvents : function() 
28680     {
28681         this.el.on('mouseover', this.onMouseOver, this);
28682         this.el.on('mouseout', this.onMouseOut, this);
28683         
28684         this.el.select('a', true).first().on('click', this.onClick, this);
28685         
28686     },
28687     
28688     onClick : function(e)
28689     {
28690         if(this.preventDefault){
28691             e.preventDefault();
28692         }
28693         
28694         this.fireEvent("click", this, e);
28695     },
28696     
28697     onMouseOver : function(e)
28698     {
28699         if(this.submenu && this.pos == 'left'){
28700             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28701         }
28702         
28703         this.fireEvent("mouseover", this, e);
28704     },
28705     
28706     onMouseOut : function(e)
28707     {
28708         this.fireEvent("mouseout", this, e);
28709     }
28710 });
28711
28712  
28713
28714  /*
28715  * - LGPL
28716  *
28717  * menu separator
28718  * 
28719  */
28720 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28721
28722 /**
28723  * @class Roo.bootstrap.menu.Separator
28724  * @extends Roo.bootstrap.Component
28725  * Bootstrap Separator class
28726  * 
28727  * @constructor
28728  * Create a new Separator
28729  * @param {Object} config The config object
28730  */
28731
28732
28733 Roo.bootstrap.menu.Separator = function(config){
28734     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28735 };
28736
28737 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28738     
28739     getAutoCreate : function(){
28740         var cfg = {
28741             tag : 'li',
28742             cls: 'divider'
28743         };
28744         
28745         return cfg;
28746     }
28747    
28748 });
28749
28750  
28751
28752  /*
28753  * - LGPL
28754  *
28755  * Tooltip
28756  * 
28757  */
28758
28759 /**
28760  * @class Roo.bootstrap.Tooltip
28761  * Bootstrap Tooltip class
28762  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28763  * to determine which dom element triggers the tooltip.
28764  * 
28765  * It needs to add support for additional attributes like tooltip-position
28766  * 
28767  * @constructor
28768  * Create a new Toolti
28769  * @param {Object} config The config object
28770  */
28771
28772 Roo.bootstrap.Tooltip = function(config){
28773     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28774     
28775     this.alignment = Roo.bootstrap.Tooltip.alignment;
28776     
28777     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28778         this.alignment = config.alignment;
28779     }
28780     
28781 };
28782
28783 Roo.apply(Roo.bootstrap.Tooltip, {
28784     /**
28785      * @function init initialize tooltip monitoring.
28786      * @static
28787      */
28788     currentEl : false,
28789     currentTip : false,
28790     currentRegion : false,
28791     
28792     //  init : delay?
28793     
28794     init : function()
28795     {
28796         Roo.get(document).on('mouseover', this.enter ,this);
28797         Roo.get(document).on('mouseout', this.leave, this);
28798          
28799         
28800         this.currentTip = new Roo.bootstrap.Tooltip();
28801     },
28802     
28803     enter : function(ev)
28804     {
28805         var dom = ev.getTarget();
28806         
28807         //Roo.log(['enter',dom]);
28808         var el = Roo.fly(dom);
28809         if (this.currentEl) {
28810             //Roo.log(dom);
28811             //Roo.log(this.currentEl);
28812             //Roo.log(this.currentEl.contains(dom));
28813             if (this.currentEl == el) {
28814                 return;
28815             }
28816             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28817                 return;
28818             }
28819
28820         }
28821         
28822         if (this.currentTip.el) {
28823             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28824         }    
28825         //Roo.log(ev);
28826         
28827         if(!el || el.dom == document){
28828             return;
28829         }
28830         
28831         var bindEl = el;
28832         
28833         // you can not look for children, as if el is the body.. then everythign is the child..
28834         if (!el.attr('tooltip')) { //
28835             if (!el.select("[tooltip]").elements.length) {
28836                 return;
28837             }
28838             // is the mouse over this child...?
28839             bindEl = el.select("[tooltip]").first();
28840             var xy = ev.getXY();
28841             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28842                 //Roo.log("not in region.");
28843                 return;
28844             }
28845             //Roo.log("child element over..");
28846             
28847         }
28848         this.currentEl = bindEl;
28849         this.currentTip.bind(bindEl);
28850         this.currentRegion = Roo.lib.Region.getRegion(dom);
28851         this.currentTip.enter();
28852         
28853     },
28854     leave : function(ev)
28855     {
28856         var dom = ev.getTarget();
28857         //Roo.log(['leave',dom]);
28858         if (!this.currentEl) {
28859             return;
28860         }
28861         
28862         
28863         if (dom != this.currentEl.dom) {
28864             return;
28865         }
28866         var xy = ev.getXY();
28867         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28868             return;
28869         }
28870         // only activate leave if mouse cursor is outside... bounding box..
28871         
28872         
28873         
28874         
28875         if (this.currentTip) {
28876             this.currentTip.leave();
28877         }
28878         //Roo.log('clear currentEl');
28879         this.currentEl = false;
28880         
28881         
28882     },
28883     alignment : {
28884         'left' : ['r-l', [-2,0], 'right'],
28885         'right' : ['l-r', [2,0], 'left'],
28886         'bottom' : ['t-b', [0,2], 'top'],
28887         'top' : [ 'b-t', [0,-2], 'bottom']
28888     }
28889     
28890 });
28891
28892
28893 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28894     
28895     
28896     bindEl : false,
28897     
28898     delay : null, // can be { show : 300 , hide: 500}
28899     
28900     timeout : null,
28901     
28902     hoverState : null, //???
28903     
28904     placement : 'bottom', 
28905     
28906     alignment : false,
28907     
28908     getAutoCreate : function(){
28909     
28910         var cfg = {
28911            cls : 'tooltip',   
28912            role : 'tooltip',
28913            cn : [
28914                 {
28915                     cls : 'tooltip-arrow arrow'
28916                 },
28917                 {
28918                     cls : 'tooltip-inner'
28919                 }
28920            ]
28921         };
28922         
28923         return cfg;
28924     },
28925     bind : function(el)
28926     {
28927         this.bindEl = el;
28928     },
28929     
28930     initEvents : function()
28931     {
28932         this.arrowEl = this.el.select('.arrow', true).first();
28933         this.innerEl = this.el.select('.tooltip-inner', true).first();
28934     },
28935     
28936     enter : function () {
28937        
28938         if (this.timeout != null) {
28939             clearTimeout(this.timeout);
28940         }
28941         
28942         this.hoverState = 'in';
28943          //Roo.log("enter - show");
28944         if (!this.delay || !this.delay.show) {
28945             this.show();
28946             return;
28947         }
28948         var _t = this;
28949         this.timeout = setTimeout(function () {
28950             if (_t.hoverState == 'in') {
28951                 _t.show();
28952             }
28953         }, this.delay.show);
28954     },
28955     leave : function()
28956     {
28957         clearTimeout(this.timeout);
28958     
28959         this.hoverState = 'out';
28960          if (!this.delay || !this.delay.hide) {
28961             this.hide();
28962             return;
28963         }
28964        
28965         var _t = this;
28966         this.timeout = setTimeout(function () {
28967             //Roo.log("leave - timeout");
28968             
28969             if (_t.hoverState == 'out') {
28970                 _t.hide();
28971                 Roo.bootstrap.Tooltip.currentEl = false;
28972             }
28973         }, delay);
28974     },
28975     
28976     show : function (msg)
28977     {
28978         if (!this.el) {
28979             this.render(document.body);
28980         }
28981         // set content.
28982         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28983         
28984         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28985         
28986         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28987         
28988         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28989                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28990         
28991         var placement = typeof this.placement == 'function' ?
28992             this.placement.call(this, this.el, on_el) :
28993             this.placement;
28994             
28995         var autoToken = /\s?auto?\s?/i;
28996         var autoPlace = autoToken.test(placement);
28997         if (autoPlace) {
28998             placement = placement.replace(autoToken, '') || 'top';
28999         }
29000         
29001         //this.el.detach()
29002         //this.el.setXY([0,0]);
29003         this.el.show();
29004         //this.el.dom.style.display='block';
29005         
29006         //this.el.appendTo(on_el);
29007         
29008         var p = this.getPosition();
29009         var box = this.el.getBox();
29010         
29011         if (autoPlace) {
29012             // fixme..
29013         }
29014         
29015         var align = this.alignment[placement];
29016         
29017         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
29018         
29019         if(placement == 'top' || placement == 'bottom'){
29020             if(xy[0] < 0){
29021                 placement = 'right';
29022             }
29023             
29024             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
29025                 placement = 'left';
29026             }
29027             
29028             var scroll = Roo.select('body', true).first().getScroll();
29029             
29030             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
29031                 placement = 'top';
29032             }
29033             
29034             align = this.alignment[placement];
29035             
29036             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
29037             
29038         }
29039         
29040         this.el.alignTo(this.bindEl, align[0],align[1]);
29041         //var arrow = this.el.select('.arrow',true).first();
29042         //arrow.set(align[2], 
29043         
29044         this.el.addClass(placement);
29045         this.el.addClass("bs-tooltip-"+ placement);
29046         
29047         this.el.addClass('in fade show');
29048         
29049         this.hoverState = null;
29050         
29051         if (this.el.hasClass('fade')) {
29052             // fade it?
29053         }
29054         
29055         
29056         
29057         
29058         
29059     },
29060     hide : function()
29061     {
29062          
29063         if (!this.el) {
29064             return;
29065         }
29066         //this.el.setXY([0,0]);
29067         this.el.removeClass(['show', 'in']);
29068         //this.el.hide();
29069         
29070     }
29071     
29072 });
29073  
29074
29075  /*
29076  * - LGPL
29077  *
29078  * Location Picker
29079  * 
29080  */
29081
29082 /**
29083  * @class Roo.bootstrap.LocationPicker
29084  * @extends Roo.bootstrap.Component
29085  * Bootstrap LocationPicker class
29086  * @cfg {Number} latitude Position when init default 0
29087  * @cfg {Number} longitude Position when init default 0
29088  * @cfg {Number} zoom default 15
29089  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29090  * @cfg {Boolean} mapTypeControl default false
29091  * @cfg {Boolean} disableDoubleClickZoom default false
29092  * @cfg {Boolean} scrollwheel default true
29093  * @cfg {Boolean} streetViewControl default false
29094  * @cfg {Number} radius default 0
29095  * @cfg {String} locationName
29096  * @cfg {Boolean} draggable default true
29097  * @cfg {Boolean} enableAutocomplete default false
29098  * @cfg {Boolean} enableReverseGeocode default true
29099  * @cfg {String} markerTitle
29100  * 
29101  * @constructor
29102  * Create a new LocationPicker
29103  * @param {Object} config The config object
29104  */
29105
29106
29107 Roo.bootstrap.LocationPicker = function(config){
29108     
29109     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29110     
29111     this.addEvents({
29112         /**
29113          * @event initial
29114          * Fires when the picker initialized.
29115          * @param {Roo.bootstrap.LocationPicker} this
29116          * @param {Google Location} location
29117          */
29118         initial : true,
29119         /**
29120          * @event positionchanged
29121          * Fires when the picker position changed.
29122          * @param {Roo.bootstrap.LocationPicker} this
29123          * @param {Google Location} location
29124          */
29125         positionchanged : true,
29126         /**
29127          * @event resize
29128          * Fires when the map resize.
29129          * @param {Roo.bootstrap.LocationPicker} this
29130          */
29131         resize : true,
29132         /**
29133          * @event show
29134          * Fires when the map show.
29135          * @param {Roo.bootstrap.LocationPicker} this
29136          */
29137         show : true,
29138         /**
29139          * @event hide
29140          * Fires when the map hide.
29141          * @param {Roo.bootstrap.LocationPicker} this
29142          */
29143         hide : true,
29144         /**
29145          * @event mapClick
29146          * Fires when click the map.
29147          * @param {Roo.bootstrap.LocationPicker} this
29148          * @param {Map event} e
29149          */
29150         mapClick : true,
29151         /**
29152          * @event mapRightClick
29153          * Fires when right click the map.
29154          * @param {Roo.bootstrap.LocationPicker} this
29155          * @param {Map event} e
29156          */
29157         mapRightClick : true,
29158         /**
29159          * @event markerClick
29160          * Fires when click the marker.
29161          * @param {Roo.bootstrap.LocationPicker} this
29162          * @param {Map event} e
29163          */
29164         markerClick : true,
29165         /**
29166          * @event markerRightClick
29167          * Fires when right click the marker.
29168          * @param {Roo.bootstrap.LocationPicker} this
29169          * @param {Map event} e
29170          */
29171         markerRightClick : true,
29172         /**
29173          * @event OverlayViewDraw
29174          * Fires when OverlayView Draw
29175          * @param {Roo.bootstrap.LocationPicker} this
29176          */
29177         OverlayViewDraw : true,
29178         /**
29179          * @event OverlayViewOnAdd
29180          * Fires when OverlayView Draw
29181          * @param {Roo.bootstrap.LocationPicker} this
29182          */
29183         OverlayViewOnAdd : true,
29184         /**
29185          * @event OverlayViewOnRemove
29186          * Fires when OverlayView Draw
29187          * @param {Roo.bootstrap.LocationPicker} this
29188          */
29189         OverlayViewOnRemove : true,
29190         /**
29191          * @event OverlayViewShow
29192          * Fires when OverlayView Draw
29193          * @param {Roo.bootstrap.LocationPicker} this
29194          * @param {Pixel} cpx
29195          */
29196         OverlayViewShow : true,
29197         /**
29198          * @event OverlayViewHide
29199          * Fires when OverlayView Draw
29200          * @param {Roo.bootstrap.LocationPicker} this
29201          */
29202         OverlayViewHide : true,
29203         /**
29204          * @event loadexception
29205          * Fires when load google lib failed.
29206          * @param {Roo.bootstrap.LocationPicker} this
29207          */
29208         loadexception : true
29209     });
29210         
29211 };
29212
29213 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29214     
29215     gMapContext: false,
29216     
29217     latitude: 0,
29218     longitude: 0,
29219     zoom: 15,
29220     mapTypeId: false,
29221     mapTypeControl: false,
29222     disableDoubleClickZoom: false,
29223     scrollwheel: true,
29224     streetViewControl: false,
29225     radius: 0,
29226     locationName: '',
29227     draggable: true,
29228     enableAutocomplete: false,
29229     enableReverseGeocode: true,
29230     markerTitle: '',
29231     
29232     getAutoCreate: function()
29233     {
29234
29235         var cfg = {
29236             tag: 'div',
29237             cls: 'roo-location-picker'
29238         };
29239         
29240         return cfg
29241     },
29242     
29243     initEvents: function(ct, position)
29244     {       
29245         if(!this.el.getWidth() || this.isApplied()){
29246             return;
29247         }
29248         
29249         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29250         
29251         this.initial();
29252     },
29253     
29254     initial: function()
29255     {
29256         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29257             this.fireEvent('loadexception', this);
29258             return;
29259         }
29260         
29261         if(!this.mapTypeId){
29262             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29263         }
29264         
29265         this.gMapContext = this.GMapContext();
29266         
29267         this.initOverlayView();
29268         
29269         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29270         
29271         var _this = this;
29272                 
29273         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29274             _this.setPosition(_this.gMapContext.marker.position);
29275         });
29276         
29277         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29278             _this.fireEvent('mapClick', this, event);
29279             
29280         });
29281
29282         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29283             _this.fireEvent('mapRightClick', this, event);
29284             
29285         });
29286         
29287         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29288             _this.fireEvent('markerClick', this, event);
29289             
29290         });
29291
29292         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29293             _this.fireEvent('markerRightClick', this, event);
29294             
29295         });
29296         
29297         this.setPosition(this.gMapContext.location);
29298         
29299         this.fireEvent('initial', this, this.gMapContext.location);
29300     },
29301     
29302     initOverlayView: function()
29303     {
29304         var _this = this;
29305         
29306         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29307             
29308             draw: function()
29309             {
29310                 _this.fireEvent('OverlayViewDraw', _this);
29311             },
29312             
29313             onAdd: function()
29314             {
29315                 _this.fireEvent('OverlayViewOnAdd', _this);
29316             },
29317             
29318             onRemove: function()
29319             {
29320                 _this.fireEvent('OverlayViewOnRemove', _this);
29321             },
29322             
29323             show: function(cpx)
29324             {
29325                 _this.fireEvent('OverlayViewShow', _this, cpx);
29326             },
29327             
29328             hide: function()
29329             {
29330                 _this.fireEvent('OverlayViewHide', _this);
29331             }
29332             
29333         });
29334     },
29335     
29336     fromLatLngToContainerPixel: function(event)
29337     {
29338         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29339     },
29340     
29341     isApplied: function() 
29342     {
29343         return this.getGmapContext() == false ? false : true;
29344     },
29345     
29346     getGmapContext: function() 
29347     {
29348         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29349     },
29350     
29351     GMapContext: function() 
29352     {
29353         var position = new google.maps.LatLng(this.latitude, this.longitude);
29354         
29355         var _map = new google.maps.Map(this.el.dom, {
29356             center: position,
29357             zoom: this.zoom,
29358             mapTypeId: this.mapTypeId,
29359             mapTypeControl: this.mapTypeControl,
29360             disableDoubleClickZoom: this.disableDoubleClickZoom,
29361             scrollwheel: this.scrollwheel,
29362             streetViewControl: this.streetViewControl,
29363             locationName: this.locationName,
29364             draggable: this.draggable,
29365             enableAutocomplete: this.enableAutocomplete,
29366             enableReverseGeocode: this.enableReverseGeocode
29367         });
29368         
29369         var _marker = new google.maps.Marker({
29370             position: position,
29371             map: _map,
29372             title: this.markerTitle,
29373             draggable: this.draggable
29374         });
29375         
29376         return {
29377             map: _map,
29378             marker: _marker,
29379             circle: null,
29380             location: position,
29381             radius: this.radius,
29382             locationName: this.locationName,
29383             addressComponents: {
29384                 formatted_address: null,
29385                 addressLine1: null,
29386                 addressLine2: null,
29387                 streetName: null,
29388                 streetNumber: null,
29389                 city: null,
29390                 district: null,
29391                 state: null,
29392                 stateOrProvince: null
29393             },
29394             settings: this,
29395             domContainer: this.el.dom,
29396             geodecoder: new google.maps.Geocoder()
29397         };
29398     },
29399     
29400     drawCircle: function(center, radius, options) 
29401     {
29402         if (this.gMapContext.circle != null) {
29403             this.gMapContext.circle.setMap(null);
29404         }
29405         if (radius > 0) {
29406             radius *= 1;
29407             options = Roo.apply({}, options, {
29408                 strokeColor: "#0000FF",
29409                 strokeOpacity: .35,
29410                 strokeWeight: 2,
29411                 fillColor: "#0000FF",
29412                 fillOpacity: .2
29413             });
29414             
29415             options.map = this.gMapContext.map;
29416             options.radius = radius;
29417             options.center = center;
29418             this.gMapContext.circle = new google.maps.Circle(options);
29419             return this.gMapContext.circle;
29420         }
29421         
29422         return null;
29423     },
29424     
29425     setPosition: function(location) 
29426     {
29427         this.gMapContext.location = location;
29428         this.gMapContext.marker.setPosition(location);
29429         this.gMapContext.map.panTo(location);
29430         this.drawCircle(location, this.gMapContext.radius, {});
29431         
29432         var _this = this;
29433         
29434         if (this.gMapContext.settings.enableReverseGeocode) {
29435             this.gMapContext.geodecoder.geocode({
29436                 latLng: this.gMapContext.location
29437             }, function(results, status) {
29438                 
29439                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29440                     _this.gMapContext.locationName = results[0].formatted_address;
29441                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29442                     
29443                     _this.fireEvent('positionchanged', this, location);
29444                 }
29445             });
29446             
29447             return;
29448         }
29449         
29450         this.fireEvent('positionchanged', this, location);
29451     },
29452     
29453     resize: function()
29454     {
29455         google.maps.event.trigger(this.gMapContext.map, "resize");
29456         
29457         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29458         
29459         this.fireEvent('resize', this);
29460     },
29461     
29462     setPositionByLatLng: function(latitude, longitude)
29463     {
29464         this.setPosition(new google.maps.LatLng(latitude, longitude));
29465     },
29466     
29467     getCurrentPosition: function() 
29468     {
29469         return {
29470             latitude: this.gMapContext.location.lat(),
29471             longitude: this.gMapContext.location.lng()
29472         };
29473     },
29474     
29475     getAddressName: function() 
29476     {
29477         return this.gMapContext.locationName;
29478     },
29479     
29480     getAddressComponents: function() 
29481     {
29482         return this.gMapContext.addressComponents;
29483     },
29484     
29485     address_component_from_google_geocode: function(address_components) 
29486     {
29487         var result = {};
29488         
29489         for (var i = 0; i < address_components.length; i++) {
29490             var component = address_components[i];
29491             if (component.types.indexOf("postal_code") >= 0) {
29492                 result.postalCode = component.short_name;
29493             } else if (component.types.indexOf("street_number") >= 0) {
29494                 result.streetNumber = component.short_name;
29495             } else if (component.types.indexOf("route") >= 0) {
29496                 result.streetName = component.short_name;
29497             } else if (component.types.indexOf("neighborhood") >= 0) {
29498                 result.city = component.short_name;
29499             } else if (component.types.indexOf("locality") >= 0) {
29500                 result.city = component.short_name;
29501             } else if (component.types.indexOf("sublocality") >= 0) {
29502                 result.district = component.short_name;
29503             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29504                 result.stateOrProvince = component.short_name;
29505             } else if (component.types.indexOf("country") >= 0) {
29506                 result.country = component.short_name;
29507             }
29508         }
29509         
29510         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29511         result.addressLine2 = "";
29512         return result;
29513     },
29514     
29515     setZoomLevel: function(zoom)
29516     {
29517         this.gMapContext.map.setZoom(zoom);
29518     },
29519     
29520     show: function()
29521     {
29522         if(!this.el){
29523             return;
29524         }
29525         
29526         this.el.show();
29527         
29528         this.resize();
29529         
29530         this.fireEvent('show', this);
29531     },
29532     
29533     hide: function()
29534     {
29535         if(!this.el){
29536             return;
29537         }
29538         
29539         this.el.hide();
29540         
29541         this.fireEvent('hide', this);
29542     }
29543     
29544 });
29545
29546 Roo.apply(Roo.bootstrap.LocationPicker, {
29547     
29548     OverlayView : function(map, options)
29549     {
29550         options = options || {};
29551         
29552         this.setMap(map);
29553     }
29554     
29555     
29556 });/**
29557  * @class Roo.bootstrap.Alert
29558  * @extends Roo.bootstrap.Component
29559  * Bootstrap Alert class - shows an alert area box
29560  * eg
29561  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29562   Enter a valid email address
29563 </div>
29564  * @licence LGPL
29565  * @cfg {String} title The title of alert
29566  * @cfg {String} html The content of alert
29567  * @cfg {String} weight (  success | info | warning | danger )
29568  * @cfg {String} faicon font-awesomeicon
29569  * 
29570  * @constructor
29571  * Create a new alert
29572  * @param {Object} config The config object
29573  */
29574
29575
29576 Roo.bootstrap.Alert = function(config){
29577     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29578     
29579 };
29580
29581 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29582     
29583     title: '',
29584     html: '',
29585     weight: false,
29586     faicon: false,
29587     
29588     getAutoCreate : function()
29589     {
29590         
29591         var cfg = {
29592             tag : 'div',
29593             cls : 'alert',
29594             cn : [
29595                 {
29596                     tag : 'i',
29597                     cls : 'roo-alert-icon'
29598                     
29599                 },
29600                 {
29601                     tag : 'b',
29602                     cls : 'roo-alert-title',
29603                     html : this.title
29604                 },
29605                 {
29606                     tag : 'span',
29607                     cls : 'roo-alert-text',
29608                     html : this.html
29609                 }
29610             ]
29611         };
29612         
29613         if(this.faicon){
29614             cfg.cn[0].cls += ' fa ' + this.faicon;
29615         }
29616         
29617         if(this.weight){
29618             cfg.cls += ' alert-' + this.weight;
29619         }
29620         
29621         return cfg;
29622     },
29623     
29624     initEvents: function() 
29625     {
29626         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29627     },
29628     
29629     setTitle : function(str)
29630     {
29631         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29632     },
29633     
29634     setText : function(str)
29635     {
29636         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29637     },
29638     
29639     setWeight : function(weight)
29640     {
29641         if(this.weight){
29642             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29643         }
29644         
29645         this.weight = weight;
29646         
29647         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29648     },
29649     
29650     setIcon : function(icon)
29651     {
29652         if(this.faicon){
29653             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29654         }
29655         
29656         this.faicon = icon;
29657         
29658         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29659     },
29660     
29661     hide: function() 
29662     {
29663         this.el.hide();   
29664     },
29665     
29666     show: function() 
29667     {  
29668         this.el.show();   
29669     }
29670     
29671 });
29672
29673  
29674 /*
29675 * Licence: LGPL
29676 */
29677
29678 /**
29679  * @class Roo.bootstrap.UploadCropbox
29680  * @extends Roo.bootstrap.Component
29681  * Bootstrap UploadCropbox class
29682  * @cfg {String} emptyText show when image has been loaded
29683  * @cfg {String} rotateNotify show when image too small to rotate
29684  * @cfg {Number} errorTimeout default 3000
29685  * @cfg {Number} minWidth default 300
29686  * @cfg {Number} minHeight default 300
29687  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29688  * @cfg {Boolean} isDocument (true|false) default false
29689  * @cfg {String} url action url
29690  * @cfg {String} paramName default 'imageUpload'
29691  * @cfg {String} method default POST
29692  * @cfg {Boolean} loadMask (true|false) default true
29693  * @cfg {Boolean} loadingText default 'Loading...'
29694  * 
29695  * @constructor
29696  * Create a new UploadCropbox
29697  * @param {Object} config The config object
29698  */
29699
29700 Roo.bootstrap.UploadCropbox = function(config){
29701     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29702     
29703     this.addEvents({
29704         /**
29705          * @event beforeselectfile
29706          * Fire before select file
29707          * @param {Roo.bootstrap.UploadCropbox} this
29708          */
29709         "beforeselectfile" : true,
29710         /**
29711          * @event initial
29712          * Fire after initEvent
29713          * @param {Roo.bootstrap.UploadCropbox} this
29714          */
29715         "initial" : true,
29716         /**
29717          * @event crop
29718          * Fire after initEvent
29719          * @param {Roo.bootstrap.UploadCropbox} this
29720          * @param {String} data
29721          */
29722         "crop" : true,
29723         /**
29724          * @event prepare
29725          * Fire when preparing the file data
29726          * @param {Roo.bootstrap.UploadCropbox} this
29727          * @param {Object} file
29728          */
29729         "prepare" : true,
29730         /**
29731          * @event exception
29732          * Fire when get exception
29733          * @param {Roo.bootstrap.UploadCropbox} this
29734          * @param {XMLHttpRequest} xhr
29735          */
29736         "exception" : true,
29737         /**
29738          * @event beforeloadcanvas
29739          * Fire before load the canvas
29740          * @param {Roo.bootstrap.UploadCropbox} this
29741          * @param {String} src
29742          */
29743         "beforeloadcanvas" : true,
29744         /**
29745          * @event trash
29746          * Fire when trash image
29747          * @param {Roo.bootstrap.UploadCropbox} this
29748          */
29749         "trash" : true,
29750         /**
29751          * @event download
29752          * Fire when download the image
29753          * @param {Roo.bootstrap.UploadCropbox} this
29754          */
29755         "download" : true,
29756         /**
29757          * @event footerbuttonclick
29758          * Fire when footerbuttonclick
29759          * @param {Roo.bootstrap.UploadCropbox} this
29760          * @param {String} type
29761          */
29762         "footerbuttonclick" : true,
29763         /**
29764          * @event resize
29765          * Fire when resize
29766          * @param {Roo.bootstrap.UploadCropbox} this
29767          */
29768         "resize" : true,
29769         /**
29770          * @event rotate
29771          * Fire when rotate the image
29772          * @param {Roo.bootstrap.UploadCropbox} this
29773          * @param {String} pos
29774          */
29775         "rotate" : true,
29776         /**
29777          * @event inspect
29778          * Fire when inspect the file
29779          * @param {Roo.bootstrap.UploadCropbox} this
29780          * @param {Object} file
29781          */
29782         "inspect" : true,
29783         /**
29784          * @event upload
29785          * Fire when xhr upload the file
29786          * @param {Roo.bootstrap.UploadCropbox} this
29787          * @param {Object} data
29788          */
29789         "upload" : true,
29790         /**
29791          * @event arrange
29792          * Fire when arrange the file data
29793          * @param {Roo.bootstrap.UploadCropbox} this
29794          * @param {Object} formData
29795          */
29796         "arrange" : true
29797     });
29798     
29799     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29800 };
29801
29802 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29803     
29804     emptyText : 'Click to upload image',
29805     rotateNotify : 'Image is too small to rotate',
29806     errorTimeout : 3000,
29807     scale : 0,
29808     baseScale : 1,
29809     rotate : 0,
29810     dragable : false,
29811     pinching : false,
29812     mouseX : 0,
29813     mouseY : 0,
29814     cropData : false,
29815     minWidth : 300,
29816     minHeight : 300,
29817     file : false,
29818     exif : {},
29819     baseRotate : 1,
29820     cropType : 'image/jpeg',
29821     buttons : false,
29822     canvasLoaded : false,
29823     isDocument : false,
29824     method : 'POST',
29825     paramName : 'imageUpload',
29826     loadMask : true,
29827     loadingText : 'Loading...',
29828     maskEl : false,
29829     
29830     getAutoCreate : function()
29831     {
29832         var cfg = {
29833             tag : 'div',
29834             cls : 'roo-upload-cropbox',
29835             cn : [
29836                 {
29837                     tag : 'input',
29838                     cls : 'roo-upload-cropbox-selector',
29839                     type : 'file'
29840                 },
29841                 {
29842                     tag : 'div',
29843                     cls : 'roo-upload-cropbox-body',
29844                     style : 'cursor:pointer',
29845                     cn : [
29846                         {
29847                             tag : 'div',
29848                             cls : 'roo-upload-cropbox-preview'
29849                         },
29850                         {
29851                             tag : 'div',
29852                             cls : 'roo-upload-cropbox-thumb'
29853                         },
29854                         {
29855                             tag : 'div',
29856                             cls : 'roo-upload-cropbox-empty-notify',
29857                             html : this.emptyText
29858                         },
29859                         {
29860                             tag : 'div',
29861                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29862                             html : this.rotateNotify
29863                         }
29864                     ]
29865                 },
29866                 {
29867                     tag : 'div',
29868                     cls : 'roo-upload-cropbox-footer',
29869                     cn : {
29870                         tag : 'div',
29871                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29872                         cn : []
29873                     }
29874                 }
29875             ]
29876         };
29877         
29878         return cfg;
29879     },
29880     
29881     onRender : function(ct, position)
29882     {
29883         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29884         
29885         if (this.buttons.length) {
29886             
29887             Roo.each(this.buttons, function(bb) {
29888                 
29889                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29890                 
29891                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29892                 
29893             }, this);
29894         }
29895         
29896         if(this.loadMask){
29897             this.maskEl = this.el;
29898         }
29899     },
29900     
29901     initEvents : function()
29902     {
29903         this.urlAPI = (window.createObjectURL && window) || 
29904                                 (window.URL && URL.revokeObjectURL && URL) || 
29905                                 (window.webkitURL && webkitURL);
29906                         
29907         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29908         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29909         
29910         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29911         this.selectorEl.hide();
29912         
29913         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29914         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29915         
29916         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29917         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29918         this.thumbEl.hide();
29919         
29920         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29921         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29922         
29923         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29924         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29925         this.errorEl.hide();
29926         
29927         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29928         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29929         this.footerEl.hide();
29930         
29931         this.setThumbBoxSize();
29932         
29933         this.bind();
29934         
29935         this.resize();
29936         
29937         this.fireEvent('initial', this);
29938     },
29939
29940     bind : function()
29941     {
29942         var _this = this;
29943         
29944         window.addEventListener("resize", function() { _this.resize(); } );
29945         
29946         this.bodyEl.on('click', this.beforeSelectFile, this);
29947         
29948         if(Roo.isTouch){
29949             this.bodyEl.on('touchstart', this.onTouchStart, this);
29950             this.bodyEl.on('touchmove', this.onTouchMove, this);
29951             this.bodyEl.on('touchend', this.onTouchEnd, this);
29952         }
29953         
29954         if(!Roo.isTouch){
29955             this.bodyEl.on('mousedown', this.onMouseDown, this);
29956             this.bodyEl.on('mousemove', this.onMouseMove, this);
29957             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29958             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29959             Roo.get(document).on('mouseup', this.onMouseUp, this);
29960         }
29961         
29962         this.selectorEl.on('change', this.onFileSelected, this);
29963     },
29964     
29965     reset : function()
29966     {    
29967         this.scale = 0;
29968         this.baseScale = 1;
29969         this.rotate = 0;
29970         this.baseRotate = 1;
29971         this.dragable = false;
29972         this.pinching = false;
29973         this.mouseX = 0;
29974         this.mouseY = 0;
29975         this.cropData = false;
29976         this.notifyEl.dom.innerHTML = this.emptyText;
29977         
29978         this.selectorEl.dom.value = '';
29979         
29980     },
29981     
29982     resize : function()
29983     {
29984         if(this.fireEvent('resize', this) != false){
29985             this.setThumbBoxPosition();
29986             this.setCanvasPosition();
29987         }
29988     },
29989     
29990     onFooterButtonClick : function(e, el, o, type)
29991     {
29992         switch (type) {
29993             case 'rotate-left' :
29994                 this.onRotateLeft(e);
29995                 break;
29996             case 'rotate-right' :
29997                 this.onRotateRight(e);
29998                 break;
29999             case 'picture' :
30000                 this.beforeSelectFile(e);
30001                 break;
30002             case 'trash' :
30003                 this.trash(e);
30004                 break;
30005             case 'crop' :
30006                 this.crop(e);
30007                 break;
30008             case 'download' :
30009                 this.download(e);
30010                 break;
30011             default :
30012                 break;
30013         }
30014         
30015         this.fireEvent('footerbuttonclick', this, type);
30016     },
30017     
30018     beforeSelectFile : function(e)
30019     {
30020         e.preventDefault();
30021         
30022         if(this.fireEvent('beforeselectfile', this) != false){
30023             this.selectorEl.dom.click();
30024         }
30025     },
30026     
30027     onFileSelected : function(e)
30028     {
30029         e.preventDefault();
30030         
30031         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
30032             return;
30033         }
30034         
30035         var file = this.selectorEl.dom.files[0];
30036         
30037         if(this.fireEvent('inspect', this, file) != false){
30038             this.prepare(file);
30039         }
30040         
30041     },
30042     
30043     trash : function(e)
30044     {
30045         this.fireEvent('trash', this);
30046     },
30047     
30048     download : function(e)
30049     {
30050         this.fireEvent('download', this);
30051     },
30052     
30053     loadCanvas : function(src)
30054     {   
30055         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30056             
30057             this.reset();
30058             
30059             this.imageEl = document.createElement('img');
30060             
30061             var _this = this;
30062             
30063             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30064             
30065             this.imageEl.src = src;
30066         }
30067     },
30068     
30069     onLoadCanvas : function()
30070     {   
30071         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30072         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30073         
30074         this.bodyEl.un('click', this.beforeSelectFile, this);
30075         
30076         this.notifyEl.hide();
30077         this.thumbEl.show();
30078         this.footerEl.show();
30079         
30080         this.baseRotateLevel();
30081         
30082         if(this.isDocument){
30083             this.setThumbBoxSize();
30084         }
30085         
30086         this.setThumbBoxPosition();
30087         
30088         this.baseScaleLevel();
30089         
30090         this.draw();
30091         
30092         this.resize();
30093         
30094         this.canvasLoaded = true;
30095         
30096         if(this.loadMask){
30097             this.maskEl.unmask();
30098         }
30099         
30100     },
30101     
30102     setCanvasPosition : function()
30103     {   
30104         if(!this.canvasEl){
30105             return;
30106         }
30107         
30108         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30109         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30110         
30111         this.previewEl.setLeft(pw);
30112         this.previewEl.setTop(ph);
30113         
30114     },
30115     
30116     onMouseDown : function(e)
30117     {   
30118         e.stopEvent();
30119         
30120         this.dragable = true;
30121         this.pinching = false;
30122         
30123         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30124             this.dragable = false;
30125             return;
30126         }
30127         
30128         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30129         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30130         
30131     },
30132     
30133     onMouseMove : function(e)
30134     {   
30135         e.stopEvent();
30136         
30137         if(!this.canvasLoaded){
30138             return;
30139         }
30140         
30141         if (!this.dragable){
30142             return;
30143         }
30144         
30145         var minX = Math.ceil(this.thumbEl.getLeft(true));
30146         var minY = Math.ceil(this.thumbEl.getTop(true));
30147         
30148         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30149         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30150         
30151         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30152         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30153         
30154         x = x - this.mouseX;
30155         y = y - this.mouseY;
30156         
30157         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30158         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30159         
30160         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30161         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30162         
30163         this.previewEl.setLeft(bgX);
30164         this.previewEl.setTop(bgY);
30165         
30166         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30167         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30168     },
30169     
30170     onMouseUp : function(e)
30171     {   
30172         e.stopEvent();
30173         
30174         this.dragable = false;
30175     },
30176     
30177     onMouseWheel : function(e)
30178     {   
30179         e.stopEvent();
30180         
30181         this.startScale = this.scale;
30182         
30183         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30184         
30185         if(!this.zoomable()){
30186             this.scale = this.startScale;
30187             return;
30188         }
30189         
30190         this.draw();
30191         
30192         return;
30193     },
30194     
30195     zoomable : function()
30196     {
30197         var minScale = this.thumbEl.getWidth() / this.minWidth;
30198         
30199         if(this.minWidth < this.minHeight){
30200             minScale = this.thumbEl.getHeight() / this.minHeight;
30201         }
30202         
30203         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30204         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30205         
30206         if(
30207                 this.isDocument &&
30208                 (this.rotate == 0 || this.rotate == 180) && 
30209                 (
30210                     width > this.imageEl.OriginWidth || 
30211                     height > this.imageEl.OriginHeight ||
30212                     (width < this.minWidth && height < this.minHeight)
30213                 )
30214         ){
30215             return false;
30216         }
30217         
30218         if(
30219                 this.isDocument &&
30220                 (this.rotate == 90 || this.rotate == 270) && 
30221                 (
30222                     width > this.imageEl.OriginWidth || 
30223                     height > this.imageEl.OriginHeight ||
30224                     (width < this.minHeight && height < this.minWidth)
30225                 )
30226         ){
30227             return false;
30228         }
30229         
30230         if(
30231                 !this.isDocument &&
30232                 (this.rotate == 0 || this.rotate == 180) && 
30233                 (
30234                     width < this.minWidth || 
30235                     width > this.imageEl.OriginWidth || 
30236                     height < this.minHeight || 
30237                     height > this.imageEl.OriginHeight
30238                 )
30239         ){
30240             return false;
30241         }
30242         
30243         if(
30244                 !this.isDocument &&
30245                 (this.rotate == 90 || this.rotate == 270) && 
30246                 (
30247                     width < this.minHeight || 
30248                     width > this.imageEl.OriginWidth || 
30249                     height < this.minWidth || 
30250                     height > this.imageEl.OriginHeight
30251                 )
30252         ){
30253             return false;
30254         }
30255         
30256         return true;
30257         
30258     },
30259     
30260     onRotateLeft : function(e)
30261     {   
30262         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30263             
30264             var minScale = this.thumbEl.getWidth() / this.minWidth;
30265             
30266             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30267             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30268             
30269             this.startScale = this.scale;
30270             
30271             while (this.getScaleLevel() < minScale){
30272             
30273                 this.scale = this.scale + 1;
30274                 
30275                 if(!this.zoomable()){
30276                     break;
30277                 }
30278                 
30279                 if(
30280                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30281                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30282                 ){
30283                     continue;
30284                 }
30285                 
30286                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30287
30288                 this.draw();
30289                 
30290                 return;
30291             }
30292             
30293             this.scale = this.startScale;
30294             
30295             this.onRotateFail();
30296             
30297             return false;
30298         }
30299         
30300         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30301
30302         if(this.isDocument){
30303             this.setThumbBoxSize();
30304             this.setThumbBoxPosition();
30305             this.setCanvasPosition();
30306         }
30307         
30308         this.draw();
30309         
30310         this.fireEvent('rotate', this, 'left');
30311         
30312     },
30313     
30314     onRotateRight : function(e)
30315     {
30316         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30317             
30318             var minScale = this.thumbEl.getWidth() / this.minWidth;
30319         
30320             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30321             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30322             
30323             this.startScale = this.scale;
30324             
30325             while (this.getScaleLevel() < minScale){
30326             
30327                 this.scale = this.scale + 1;
30328                 
30329                 if(!this.zoomable()){
30330                     break;
30331                 }
30332                 
30333                 if(
30334                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30335                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30336                 ){
30337                     continue;
30338                 }
30339                 
30340                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30341
30342                 this.draw();
30343                 
30344                 return;
30345             }
30346             
30347             this.scale = this.startScale;
30348             
30349             this.onRotateFail();
30350             
30351             return false;
30352         }
30353         
30354         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30355
30356         if(this.isDocument){
30357             this.setThumbBoxSize();
30358             this.setThumbBoxPosition();
30359             this.setCanvasPosition();
30360         }
30361         
30362         this.draw();
30363         
30364         this.fireEvent('rotate', this, 'right');
30365     },
30366     
30367     onRotateFail : function()
30368     {
30369         this.errorEl.show(true);
30370         
30371         var _this = this;
30372         
30373         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30374     },
30375     
30376     draw : function()
30377     {
30378         this.previewEl.dom.innerHTML = '';
30379         
30380         var canvasEl = document.createElement("canvas");
30381         
30382         var contextEl = canvasEl.getContext("2d");
30383         
30384         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30385         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30386         var center = this.imageEl.OriginWidth / 2;
30387         
30388         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30389             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30390             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30391             center = this.imageEl.OriginHeight / 2;
30392         }
30393         
30394         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30395         
30396         contextEl.translate(center, center);
30397         contextEl.rotate(this.rotate * Math.PI / 180);
30398
30399         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30400         
30401         this.canvasEl = document.createElement("canvas");
30402         
30403         this.contextEl = this.canvasEl.getContext("2d");
30404         
30405         switch (this.rotate) {
30406             case 0 :
30407                 
30408                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30409                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30410                 
30411                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30412                 
30413                 break;
30414             case 90 : 
30415                 
30416                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30417                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30418                 
30419                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30420                     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);
30421                     break;
30422                 }
30423                 
30424                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30425                 
30426                 break;
30427             case 180 :
30428                 
30429                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30430                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30431                 
30432                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30433                     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);
30434                     break;
30435                 }
30436                 
30437                 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);
30438                 
30439                 break;
30440             case 270 :
30441                 
30442                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30443                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30444         
30445                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30446                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30447                     break;
30448                 }
30449                 
30450                 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);
30451                 
30452                 break;
30453             default : 
30454                 break;
30455         }
30456         
30457         this.previewEl.appendChild(this.canvasEl);
30458         
30459         this.setCanvasPosition();
30460     },
30461     
30462     crop : function()
30463     {
30464         if(!this.canvasLoaded){
30465             return;
30466         }
30467         
30468         var imageCanvas = document.createElement("canvas");
30469         
30470         var imageContext = imageCanvas.getContext("2d");
30471         
30472         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30473         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30474         
30475         var center = imageCanvas.width / 2;
30476         
30477         imageContext.translate(center, center);
30478         
30479         imageContext.rotate(this.rotate * Math.PI / 180);
30480         
30481         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30482         
30483         var canvas = document.createElement("canvas");
30484         
30485         var context = canvas.getContext("2d");
30486                 
30487         canvas.width = this.minWidth;
30488         canvas.height = this.minHeight;
30489
30490         switch (this.rotate) {
30491             case 0 :
30492                 
30493                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30494                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30495                 
30496                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30497                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30498                 
30499                 var targetWidth = this.minWidth - 2 * x;
30500                 var targetHeight = this.minHeight - 2 * y;
30501                 
30502                 var scale = 1;
30503                 
30504                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30505                     scale = targetWidth / width;
30506                 }
30507                 
30508                 if(x > 0 && y == 0){
30509                     scale = targetHeight / height;
30510                 }
30511                 
30512                 if(x > 0 && y > 0){
30513                     scale = targetWidth / width;
30514                     
30515                     if(width < height){
30516                         scale = targetHeight / height;
30517                     }
30518                 }
30519                 
30520                 context.scale(scale, scale);
30521                 
30522                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30523                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30524
30525                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30526                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30527
30528                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30529                 
30530                 break;
30531             case 90 : 
30532                 
30533                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30534                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30535                 
30536                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30537                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30538                 
30539                 var targetWidth = this.minWidth - 2 * x;
30540                 var targetHeight = this.minHeight - 2 * y;
30541                 
30542                 var scale = 1;
30543                 
30544                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30545                     scale = targetWidth / width;
30546                 }
30547                 
30548                 if(x > 0 && y == 0){
30549                     scale = targetHeight / height;
30550                 }
30551                 
30552                 if(x > 0 && y > 0){
30553                     scale = targetWidth / width;
30554                     
30555                     if(width < height){
30556                         scale = targetHeight / height;
30557                     }
30558                 }
30559                 
30560                 context.scale(scale, scale);
30561                 
30562                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30563                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30564
30565                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30566                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30567                 
30568                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30569                 
30570                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30571                 
30572                 break;
30573             case 180 :
30574                 
30575                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30576                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30577                 
30578                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30579                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30580                 
30581                 var targetWidth = this.minWidth - 2 * x;
30582                 var targetHeight = this.minHeight - 2 * y;
30583                 
30584                 var scale = 1;
30585                 
30586                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30587                     scale = targetWidth / width;
30588                 }
30589                 
30590                 if(x > 0 && y == 0){
30591                     scale = targetHeight / height;
30592                 }
30593                 
30594                 if(x > 0 && y > 0){
30595                     scale = targetWidth / width;
30596                     
30597                     if(width < height){
30598                         scale = targetHeight / height;
30599                     }
30600                 }
30601                 
30602                 context.scale(scale, scale);
30603                 
30604                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30605                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30606
30607                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30608                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30609
30610                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30611                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30612                 
30613                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30614                 
30615                 break;
30616             case 270 :
30617                 
30618                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30619                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30620                 
30621                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30622                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30623                 
30624                 var targetWidth = this.minWidth - 2 * x;
30625                 var targetHeight = this.minHeight - 2 * y;
30626                 
30627                 var scale = 1;
30628                 
30629                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30630                     scale = targetWidth / width;
30631                 }
30632                 
30633                 if(x > 0 && y == 0){
30634                     scale = targetHeight / height;
30635                 }
30636                 
30637                 if(x > 0 && y > 0){
30638                     scale = targetWidth / width;
30639                     
30640                     if(width < height){
30641                         scale = targetHeight / height;
30642                     }
30643                 }
30644                 
30645                 context.scale(scale, scale);
30646                 
30647                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30648                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30649
30650                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30651                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30652                 
30653                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30654                 
30655                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30656                 
30657                 break;
30658             default : 
30659                 break;
30660         }
30661         
30662         this.cropData = canvas.toDataURL(this.cropType);
30663         
30664         if(this.fireEvent('crop', this, this.cropData) !== false){
30665             this.process(this.file, this.cropData);
30666         }
30667         
30668         return;
30669         
30670     },
30671     
30672     setThumbBoxSize : function()
30673     {
30674         var width, height;
30675         
30676         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30677             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30678             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30679             
30680             this.minWidth = width;
30681             this.minHeight = height;
30682             
30683             if(this.rotate == 90 || this.rotate == 270){
30684                 this.minWidth = height;
30685                 this.minHeight = width;
30686             }
30687         }
30688         
30689         height = 300;
30690         width = Math.ceil(this.minWidth * height / this.minHeight);
30691         
30692         if(this.minWidth > this.minHeight){
30693             width = 300;
30694             height = Math.ceil(this.minHeight * width / this.minWidth);
30695         }
30696         
30697         this.thumbEl.setStyle({
30698             width : width + 'px',
30699             height : height + 'px'
30700         });
30701
30702         return;
30703             
30704     },
30705     
30706     setThumbBoxPosition : function()
30707     {
30708         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30709         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30710         
30711         this.thumbEl.setLeft(x);
30712         this.thumbEl.setTop(y);
30713         
30714     },
30715     
30716     baseRotateLevel : function()
30717     {
30718         this.baseRotate = 1;
30719         
30720         if(
30721                 typeof(this.exif) != 'undefined' &&
30722                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30723                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30724         ){
30725             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30726         }
30727         
30728         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30729         
30730     },
30731     
30732     baseScaleLevel : function()
30733     {
30734         var width, height;
30735         
30736         if(this.isDocument){
30737             
30738             if(this.baseRotate == 6 || this.baseRotate == 8){
30739             
30740                 height = this.thumbEl.getHeight();
30741                 this.baseScale = height / this.imageEl.OriginWidth;
30742
30743                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30744                     width = this.thumbEl.getWidth();
30745                     this.baseScale = width / this.imageEl.OriginHeight;
30746                 }
30747
30748                 return;
30749             }
30750
30751             height = this.thumbEl.getHeight();
30752             this.baseScale = height / this.imageEl.OriginHeight;
30753
30754             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30755                 width = this.thumbEl.getWidth();
30756                 this.baseScale = width / this.imageEl.OriginWidth;
30757             }
30758
30759             return;
30760         }
30761         
30762         if(this.baseRotate == 6 || this.baseRotate == 8){
30763             
30764             width = this.thumbEl.getHeight();
30765             this.baseScale = width / this.imageEl.OriginHeight;
30766             
30767             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30768                 height = this.thumbEl.getWidth();
30769                 this.baseScale = height / this.imageEl.OriginHeight;
30770             }
30771             
30772             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30773                 height = this.thumbEl.getWidth();
30774                 this.baseScale = height / this.imageEl.OriginHeight;
30775                 
30776                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30777                     width = this.thumbEl.getHeight();
30778                     this.baseScale = width / this.imageEl.OriginWidth;
30779                 }
30780             }
30781             
30782             return;
30783         }
30784         
30785         width = this.thumbEl.getWidth();
30786         this.baseScale = width / this.imageEl.OriginWidth;
30787         
30788         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30789             height = this.thumbEl.getHeight();
30790             this.baseScale = height / this.imageEl.OriginHeight;
30791         }
30792         
30793         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30794             
30795             height = this.thumbEl.getHeight();
30796             this.baseScale = height / this.imageEl.OriginHeight;
30797             
30798             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30799                 width = this.thumbEl.getWidth();
30800                 this.baseScale = width / this.imageEl.OriginWidth;
30801             }
30802             
30803         }
30804         
30805         return;
30806     },
30807     
30808     getScaleLevel : function()
30809     {
30810         return this.baseScale * Math.pow(1.1, this.scale);
30811     },
30812     
30813     onTouchStart : function(e)
30814     {
30815         if(!this.canvasLoaded){
30816             this.beforeSelectFile(e);
30817             return;
30818         }
30819         
30820         var touches = e.browserEvent.touches;
30821         
30822         if(!touches){
30823             return;
30824         }
30825         
30826         if(touches.length == 1){
30827             this.onMouseDown(e);
30828             return;
30829         }
30830         
30831         if(touches.length != 2){
30832             return;
30833         }
30834         
30835         var coords = [];
30836         
30837         for(var i = 0, finger; finger = touches[i]; i++){
30838             coords.push(finger.pageX, finger.pageY);
30839         }
30840         
30841         var x = Math.pow(coords[0] - coords[2], 2);
30842         var y = Math.pow(coords[1] - coords[3], 2);
30843         
30844         this.startDistance = Math.sqrt(x + y);
30845         
30846         this.startScale = this.scale;
30847         
30848         this.pinching = true;
30849         this.dragable = false;
30850         
30851     },
30852     
30853     onTouchMove : function(e)
30854     {
30855         if(!this.pinching && !this.dragable){
30856             return;
30857         }
30858         
30859         var touches = e.browserEvent.touches;
30860         
30861         if(!touches){
30862             return;
30863         }
30864         
30865         if(this.dragable){
30866             this.onMouseMove(e);
30867             return;
30868         }
30869         
30870         var coords = [];
30871         
30872         for(var i = 0, finger; finger = touches[i]; i++){
30873             coords.push(finger.pageX, finger.pageY);
30874         }
30875         
30876         var x = Math.pow(coords[0] - coords[2], 2);
30877         var y = Math.pow(coords[1] - coords[3], 2);
30878         
30879         this.endDistance = Math.sqrt(x + y);
30880         
30881         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30882         
30883         if(!this.zoomable()){
30884             this.scale = this.startScale;
30885             return;
30886         }
30887         
30888         this.draw();
30889         
30890     },
30891     
30892     onTouchEnd : function(e)
30893     {
30894         this.pinching = false;
30895         this.dragable = false;
30896         
30897     },
30898     
30899     process : function(file, crop)
30900     {
30901         if(this.loadMask){
30902             this.maskEl.mask(this.loadingText);
30903         }
30904         
30905         this.xhr = new XMLHttpRequest();
30906         
30907         file.xhr = this.xhr;
30908
30909         this.xhr.open(this.method, this.url, true);
30910         
30911         var headers = {
30912             "Accept": "application/json",
30913             "Cache-Control": "no-cache",
30914             "X-Requested-With": "XMLHttpRequest"
30915         };
30916         
30917         for (var headerName in headers) {
30918             var headerValue = headers[headerName];
30919             if (headerValue) {
30920                 this.xhr.setRequestHeader(headerName, headerValue);
30921             }
30922         }
30923         
30924         var _this = this;
30925         
30926         this.xhr.onload = function()
30927         {
30928             _this.xhrOnLoad(_this.xhr);
30929         }
30930         
30931         this.xhr.onerror = function()
30932         {
30933             _this.xhrOnError(_this.xhr);
30934         }
30935         
30936         var formData = new FormData();
30937
30938         formData.append('returnHTML', 'NO');
30939         
30940         if(crop){
30941             formData.append('crop', crop);
30942         }
30943         
30944         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30945             formData.append(this.paramName, file, file.name);
30946         }
30947         
30948         if(typeof(file.filename) != 'undefined'){
30949             formData.append('filename', file.filename);
30950         }
30951         
30952         if(typeof(file.mimetype) != 'undefined'){
30953             formData.append('mimetype', file.mimetype);
30954         }
30955         
30956         if(this.fireEvent('arrange', this, formData) != false){
30957             this.xhr.send(formData);
30958         };
30959     },
30960     
30961     xhrOnLoad : function(xhr)
30962     {
30963         if(this.loadMask){
30964             this.maskEl.unmask();
30965         }
30966         
30967         if (xhr.readyState !== 4) {
30968             this.fireEvent('exception', this, xhr);
30969             return;
30970         }
30971
30972         var response = Roo.decode(xhr.responseText);
30973         
30974         if(!response.success){
30975             this.fireEvent('exception', this, xhr);
30976             return;
30977         }
30978         
30979         var response = Roo.decode(xhr.responseText);
30980         
30981         this.fireEvent('upload', this, response);
30982         
30983     },
30984     
30985     xhrOnError : function()
30986     {
30987         if(this.loadMask){
30988             this.maskEl.unmask();
30989         }
30990         
30991         Roo.log('xhr on error');
30992         
30993         var response = Roo.decode(xhr.responseText);
30994           
30995         Roo.log(response);
30996         
30997     },
30998     
30999     prepare : function(file)
31000     {   
31001         if(this.loadMask){
31002             this.maskEl.mask(this.loadingText);
31003         }
31004         
31005         this.file = false;
31006         this.exif = {};
31007         
31008         if(typeof(file) === 'string'){
31009             this.loadCanvas(file);
31010             return;
31011         }
31012         
31013         if(!file || !this.urlAPI){
31014             return;
31015         }
31016         
31017         this.file = file;
31018         this.cropType = file.type;
31019         
31020         var _this = this;
31021         
31022         if(this.fireEvent('prepare', this, this.file) != false){
31023             
31024             var reader = new FileReader();
31025             
31026             reader.onload = function (e) {
31027                 if (e.target.error) {
31028                     Roo.log(e.target.error);
31029                     return;
31030                 }
31031                 
31032                 var buffer = e.target.result,
31033                     dataView = new DataView(buffer),
31034                     offset = 2,
31035                     maxOffset = dataView.byteLength - 4,
31036                     markerBytes,
31037                     markerLength;
31038                 
31039                 if (dataView.getUint16(0) === 0xffd8) {
31040                     while (offset < maxOffset) {
31041                         markerBytes = dataView.getUint16(offset);
31042                         
31043                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
31044                             markerLength = dataView.getUint16(offset + 2) + 2;
31045                             if (offset + markerLength > dataView.byteLength) {
31046                                 Roo.log('Invalid meta data: Invalid segment size.');
31047                                 break;
31048                             }
31049                             
31050                             if(markerBytes == 0xffe1){
31051                                 _this.parseExifData(
31052                                     dataView,
31053                                     offset,
31054                                     markerLength
31055                                 );
31056                             }
31057                             
31058                             offset += markerLength;
31059                             
31060                             continue;
31061                         }
31062                         
31063                         break;
31064                     }
31065                     
31066                 }
31067                 
31068                 var url = _this.urlAPI.createObjectURL(_this.file);
31069                 
31070                 _this.loadCanvas(url);
31071                 
31072                 return;
31073             }
31074             
31075             reader.readAsArrayBuffer(this.file);
31076             
31077         }
31078         
31079     },
31080     
31081     parseExifData : function(dataView, offset, length)
31082     {
31083         var tiffOffset = offset + 10,
31084             littleEndian,
31085             dirOffset;
31086     
31087         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31088             // No Exif data, might be XMP data instead
31089             return;
31090         }
31091         
31092         // Check for the ASCII code for "Exif" (0x45786966):
31093         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31094             // No Exif data, might be XMP data instead
31095             return;
31096         }
31097         if (tiffOffset + 8 > dataView.byteLength) {
31098             Roo.log('Invalid Exif data: Invalid segment size.');
31099             return;
31100         }
31101         // Check for the two null bytes:
31102         if (dataView.getUint16(offset + 8) !== 0x0000) {
31103             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31104             return;
31105         }
31106         // Check the byte alignment:
31107         switch (dataView.getUint16(tiffOffset)) {
31108         case 0x4949:
31109             littleEndian = true;
31110             break;
31111         case 0x4D4D:
31112             littleEndian = false;
31113             break;
31114         default:
31115             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31116             return;
31117         }
31118         // Check for the TIFF tag marker (0x002A):
31119         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31120             Roo.log('Invalid Exif data: Missing TIFF marker.');
31121             return;
31122         }
31123         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31124         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31125         
31126         this.parseExifTags(
31127             dataView,
31128             tiffOffset,
31129             tiffOffset + dirOffset,
31130             littleEndian
31131         );
31132     },
31133     
31134     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31135     {
31136         var tagsNumber,
31137             dirEndOffset,
31138             i;
31139         if (dirOffset + 6 > dataView.byteLength) {
31140             Roo.log('Invalid Exif data: Invalid directory offset.');
31141             return;
31142         }
31143         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31144         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31145         if (dirEndOffset + 4 > dataView.byteLength) {
31146             Roo.log('Invalid Exif data: Invalid directory size.');
31147             return;
31148         }
31149         for (i = 0; i < tagsNumber; i += 1) {
31150             this.parseExifTag(
31151                 dataView,
31152                 tiffOffset,
31153                 dirOffset + 2 + 12 * i, // tag offset
31154                 littleEndian
31155             );
31156         }
31157         // Return the offset to the next directory:
31158         return dataView.getUint32(dirEndOffset, littleEndian);
31159     },
31160     
31161     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31162     {
31163         var tag = dataView.getUint16(offset, littleEndian);
31164         
31165         this.exif[tag] = this.getExifValue(
31166             dataView,
31167             tiffOffset,
31168             offset,
31169             dataView.getUint16(offset + 2, littleEndian), // tag type
31170             dataView.getUint32(offset + 4, littleEndian), // tag length
31171             littleEndian
31172         );
31173     },
31174     
31175     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31176     {
31177         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31178             tagSize,
31179             dataOffset,
31180             values,
31181             i,
31182             str,
31183             c;
31184     
31185         if (!tagType) {
31186             Roo.log('Invalid Exif data: Invalid tag type.');
31187             return;
31188         }
31189         
31190         tagSize = tagType.size * length;
31191         // Determine if the value is contained in the dataOffset bytes,
31192         // or if the value at the dataOffset is a pointer to the actual data:
31193         dataOffset = tagSize > 4 ?
31194                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31195         if (dataOffset + tagSize > dataView.byteLength) {
31196             Roo.log('Invalid Exif data: Invalid data offset.');
31197             return;
31198         }
31199         if (length === 1) {
31200             return tagType.getValue(dataView, dataOffset, littleEndian);
31201         }
31202         values = [];
31203         for (i = 0; i < length; i += 1) {
31204             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31205         }
31206         
31207         if (tagType.ascii) {
31208             str = '';
31209             // Concatenate the chars:
31210             for (i = 0; i < values.length; i += 1) {
31211                 c = values[i];
31212                 // Ignore the terminating NULL byte(s):
31213                 if (c === '\u0000') {
31214                     break;
31215                 }
31216                 str += c;
31217             }
31218             return str;
31219         }
31220         return values;
31221     }
31222     
31223 });
31224
31225 Roo.apply(Roo.bootstrap.UploadCropbox, {
31226     tags : {
31227         'Orientation': 0x0112
31228     },
31229     
31230     Orientation: {
31231             1: 0, //'top-left',
31232 //            2: 'top-right',
31233             3: 180, //'bottom-right',
31234 //            4: 'bottom-left',
31235 //            5: 'left-top',
31236             6: 90, //'right-top',
31237 //            7: 'right-bottom',
31238             8: 270 //'left-bottom'
31239     },
31240     
31241     exifTagTypes : {
31242         // byte, 8-bit unsigned int:
31243         1: {
31244             getValue: function (dataView, dataOffset) {
31245                 return dataView.getUint8(dataOffset);
31246             },
31247             size: 1
31248         },
31249         // ascii, 8-bit byte:
31250         2: {
31251             getValue: function (dataView, dataOffset) {
31252                 return String.fromCharCode(dataView.getUint8(dataOffset));
31253             },
31254             size: 1,
31255             ascii: true
31256         },
31257         // short, 16 bit int:
31258         3: {
31259             getValue: function (dataView, dataOffset, littleEndian) {
31260                 return dataView.getUint16(dataOffset, littleEndian);
31261             },
31262             size: 2
31263         },
31264         // long, 32 bit int:
31265         4: {
31266             getValue: function (dataView, dataOffset, littleEndian) {
31267                 return dataView.getUint32(dataOffset, littleEndian);
31268             },
31269             size: 4
31270         },
31271         // rational = two long values, first is numerator, second is denominator:
31272         5: {
31273             getValue: function (dataView, dataOffset, littleEndian) {
31274                 return dataView.getUint32(dataOffset, littleEndian) /
31275                     dataView.getUint32(dataOffset + 4, littleEndian);
31276             },
31277             size: 8
31278         },
31279         // slong, 32 bit signed int:
31280         9: {
31281             getValue: function (dataView, dataOffset, littleEndian) {
31282                 return dataView.getInt32(dataOffset, littleEndian);
31283             },
31284             size: 4
31285         },
31286         // srational, two slongs, first is numerator, second is denominator:
31287         10: {
31288             getValue: function (dataView, dataOffset, littleEndian) {
31289                 return dataView.getInt32(dataOffset, littleEndian) /
31290                     dataView.getInt32(dataOffset + 4, littleEndian);
31291             },
31292             size: 8
31293         }
31294     },
31295     
31296     footer : {
31297         STANDARD : [
31298             {
31299                 tag : 'div',
31300                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31301                 action : 'rotate-left',
31302                 cn : [
31303                     {
31304                         tag : 'button',
31305                         cls : 'btn btn-default',
31306                         html : '<i class="fa fa-undo"></i>'
31307                     }
31308                 ]
31309             },
31310             {
31311                 tag : 'div',
31312                 cls : 'btn-group roo-upload-cropbox-picture',
31313                 action : 'picture',
31314                 cn : [
31315                     {
31316                         tag : 'button',
31317                         cls : 'btn btn-default',
31318                         html : '<i class="fa fa-picture-o"></i>'
31319                     }
31320                 ]
31321             },
31322             {
31323                 tag : 'div',
31324                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31325                 action : 'rotate-right',
31326                 cn : [
31327                     {
31328                         tag : 'button',
31329                         cls : 'btn btn-default',
31330                         html : '<i class="fa fa-repeat"></i>'
31331                     }
31332                 ]
31333             }
31334         ],
31335         DOCUMENT : [
31336             {
31337                 tag : 'div',
31338                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31339                 action : 'rotate-left',
31340                 cn : [
31341                     {
31342                         tag : 'button',
31343                         cls : 'btn btn-default',
31344                         html : '<i class="fa fa-undo"></i>'
31345                     }
31346                 ]
31347             },
31348             {
31349                 tag : 'div',
31350                 cls : 'btn-group roo-upload-cropbox-download',
31351                 action : 'download',
31352                 cn : [
31353                     {
31354                         tag : 'button',
31355                         cls : 'btn btn-default',
31356                         html : '<i class="fa fa-download"></i>'
31357                     }
31358                 ]
31359             },
31360             {
31361                 tag : 'div',
31362                 cls : 'btn-group roo-upload-cropbox-crop',
31363                 action : 'crop',
31364                 cn : [
31365                     {
31366                         tag : 'button',
31367                         cls : 'btn btn-default',
31368                         html : '<i class="fa fa-crop"></i>'
31369                     }
31370                 ]
31371             },
31372             {
31373                 tag : 'div',
31374                 cls : 'btn-group roo-upload-cropbox-trash',
31375                 action : 'trash',
31376                 cn : [
31377                     {
31378                         tag : 'button',
31379                         cls : 'btn btn-default',
31380                         html : '<i class="fa fa-trash"></i>'
31381                     }
31382                 ]
31383             },
31384             {
31385                 tag : 'div',
31386                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31387                 action : 'rotate-right',
31388                 cn : [
31389                     {
31390                         tag : 'button',
31391                         cls : 'btn btn-default',
31392                         html : '<i class="fa fa-repeat"></i>'
31393                     }
31394                 ]
31395             }
31396         ],
31397         ROTATOR : [
31398             {
31399                 tag : 'div',
31400                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31401                 action : 'rotate-left',
31402                 cn : [
31403                     {
31404                         tag : 'button',
31405                         cls : 'btn btn-default',
31406                         html : '<i class="fa fa-undo"></i>'
31407                     }
31408                 ]
31409             },
31410             {
31411                 tag : 'div',
31412                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31413                 action : 'rotate-right',
31414                 cn : [
31415                     {
31416                         tag : 'button',
31417                         cls : 'btn btn-default',
31418                         html : '<i class="fa fa-repeat"></i>'
31419                     }
31420                 ]
31421             }
31422         ]
31423     }
31424 });
31425
31426 /*
31427 * Licence: LGPL
31428 */
31429
31430 /**
31431  * @class Roo.bootstrap.DocumentManager
31432  * @extends Roo.bootstrap.Component
31433  * Bootstrap DocumentManager class
31434  * @cfg {String} paramName default 'imageUpload'
31435  * @cfg {String} toolTipName default 'filename'
31436  * @cfg {String} method default POST
31437  * @cfg {String} url action url
31438  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31439  * @cfg {Boolean} multiple multiple upload default true
31440  * @cfg {Number} thumbSize default 300
31441  * @cfg {String} fieldLabel
31442  * @cfg {Number} labelWidth default 4
31443  * @cfg {String} labelAlign (left|top) default left
31444  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31445 * @cfg {Number} labellg set the width of label (1-12)
31446  * @cfg {Number} labelmd set the width of label (1-12)
31447  * @cfg {Number} labelsm set the width of label (1-12)
31448  * @cfg {Number} labelxs set the width of label (1-12)
31449  * 
31450  * @constructor
31451  * Create a new DocumentManager
31452  * @param {Object} config The config object
31453  */
31454
31455 Roo.bootstrap.DocumentManager = function(config){
31456     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31457     
31458     this.files = [];
31459     this.delegates = [];
31460     
31461     this.addEvents({
31462         /**
31463          * @event initial
31464          * Fire when initial the DocumentManager
31465          * @param {Roo.bootstrap.DocumentManager} this
31466          */
31467         "initial" : true,
31468         /**
31469          * @event inspect
31470          * inspect selected file
31471          * @param {Roo.bootstrap.DocumentManager} this
31472          * @param {File} file
31473          */
31474         "inspect" : true,
31475         /**
31476          * @event exception
31477          * Fire when xhr load exception
31478          * @param {Roo.bootstrap.DocumentManager} this
31479          * @param {XMLHttpRequest} xhr
31480          */
31481         "exception" : true,
31482         /**
31483          * @event afterupload
31484          * Fire when xhr load exception
31485          * @param {Roo.bootstrap.DocumentManager} this
31486          * @param {XMLHttpRequest} xhr
31487          */
31488         "afterupload" : true,
31489         /**
31490          * @event prepare
31491          * prepare the form data
31492          * @param {Roo.bootstrap.DocumentManager} this
31493          * @param {Object} formData
31494          */
31495         "prepare" : true,
31496         /**
31497          * @event remove
31498          * Fire when remove the file
31499          * @param {Roo.bootstrap.DocumentManager} this
31500          * @param {Object} file
31501          */
31502         "remove" : true,
31503         /**
31504          * @event refresh
31505          * Fire after refresh the file
31506          * @param {Roo.bootstrap.DocumentManager} this
31507          */
31508         "refresh" : true,
31509         /**
31510          * @event click
31511          * Fire after click the image
31512          * @param {Roo.bootstrap.DocumentManager} this
31513          * @param {Object} file
31514          */
31515         "click" : true,
31516         /**
31517          * @event edit
31518          * Fire when upload a image and editable set to true
31519          * @param {Roo.bootstrap.DocumentManager} this
31520          * @param {Object} file
31521          */
31522         "edit" : true,
31523         /**
31524          * @event beforeselectfile
31525          * Fire before select file
31526          * @param {Roo.bootstrap.DocumentManager} this
31527          */
31528         "beforeselectfile" : true,
31529         /**
31530          * @event process
31531          * Fire before process file
31532          * @param {Roo.bootstrap.DocumentManager} this
31533          * @param {Object} file
31534          */
31535         "process" : true,
31536         /**
31537          * @event previewrendered
31538          * Fire when preview rendered
31539          * @param {Roo.bootstrap.DocumentManager} this
31540          * @param {Object} file
31541          */
31542         "previewrendered" : true,
31543         /**
31544          */
31545         "previewResize" : true
31546         
31547     });
31548 };
31549
31550 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31551     
31552     boxes : 0,
31553     inputName : '',
31554     thumbSize : 300,
31555     multiple : true,
31556     files : false,
31557     method : 'POST',
31558     url : '',
31559     paramName : 'imageUpload',
31560     toolTipName : 'filename',
31561     fieldLabel : '',
31562     labelWidth : 4,
31563     labelAlign : 'left',
31564     editable : true,
31565     delegates : false,
31566     xhr : false, 
31567     
31568     labellg : 0,
31569     labelmd : 0,
31570     labelsm : 0,
31571     labelxs : 0,
31572     
31573     getAutoCreate : function()
31574     {   
31575         var managerWidget = {
31576             tag : 'div',
31577             cls : 'roo-document-manager',
31578             cn : [
31579                 {
31580                     tag : 'input',
31581                     cls : 'roo-document-manager-selector',
31582                     type : 'file'
31583                 },
31584                 {
31585                     tag : 'div',
31586                     cls : 'roo-document-manager-uploader',
31587                     cn : [
31588                         {
31589                             tag : 'div',
31590                             cls : 'roo-document-manager-upload-btn',
31591                             html : '<i class="fa fa-plus"></i>'
31592                         }
31593                     ]
31594                     
31595                 }
31596             ]
31597         };
31598         
31599         var content = [
31600             {
31601                 tag : 'div',
31602                 cls : 'column col-md-12',
31603                 cn : managerWidget
31604             }
31605         ];
31606         
31607         if(this.fieldLabel.length){
31608             
31609             content = [
31610                 {
31611                     tag : 'div',
31612                     cls : 'column col-md-12',
31613                     html : this.fieldLabel
31614                 },
31615                 {
31616                     tag : 'div',
31617                     cls : 'column col-md-12',
31618                     cn : managerWidget
31619                 }
31620             ];
31621
31622             if(this.labelAlign == 'left'){
31623                 content = [
31624                     {
31625                         tag : 'div',
31626                         cls : 'column',
31627                         html : this.fieldLabel
31628                     },
31629                     {
31630                         tag : 'div',
31631                         cls : 'column',
31632                         cn : managerWidget
31633                     }
31634                 ];
31635                 
31636                 if(this.labelWidth > 12){
31637                     content[0].style = "width: " + this.labelWidth + 'px';
31638                 }
31639
31640                 if(this.labelWidth < 13 && this.labelmd == 0){
31641                     this.labelmd = this.labelWidth;
31642                 }
31643
31644                 if(this.labellg > 0){
31645                     content[0].cls += ' col-lg-' + this.labellg;
31646                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31647                 }
31648
31649                 if(this.labelmd > 0){
31650                     content[0].cls += ' col-md-' + this.labelmd;
31651                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31652                 }
31653
31654                 if(this.labelsm > 0){
31655                     content[0].cls += ' col-sm-' + this.labelsm;
31656                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31657                 }
31658
31659                 if(this.labelxs > 0){
31660                     content[0].cls += ' col-xs-' + this.labelxs;
31661                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31662                 }
31663                 
31664             }
31665         }
31666         
31667         var cfg = {
31668             tag : 'div',
31669             cls : 'row clearfix',
31670             cn : content
31671         };
31672         
31673         return cfg;
31674         
31675     },
31676     
31677     initEvents : function()
31678     {
31679         this.managerEl = this.el.select('.roo-document-manager', true).first();
31680         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31681         
31682         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31683         this.selectorEl.hide();
31684         
31685         if(this.multiple){
31686             this.selectorEl.attr('multiple', 'multiple');
31687         }
31688         
31689         this.selectorEl.on('change', this.onFileSelected, this);
31690         
31691         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31692         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31693         
31694         this.uploader.on('click', this.onUploaderClick, this);
31695         
31696         this.renderProgressDialog();
31697         
31698         var _this = this;
31699         
31700         window.addEventListener("resize", function() { _this.refresh(); } );
31701         
31702         this.fireEvent('initial', this);
31703     },
31704     
31705     renderProgressDialog : function()
31706     {
31707         var _this = this;
31708         
31709         this.progressDialog = new Roo.bootstrap.Modal({
31710             cls : 'roo-document-manager-progress-dialog',
31711             allow_close : false,
31712             animate : false,
31713             title : '',
31714             buttons : [
31715                 {
31716                     name  :'cancel',
31717                     weight : 'danger',
31718                     html : 'Cancel'
31719                 }
31720             ], 
31721             listeners : { 
31722                 btnclick : function() {
31723                     _this.uploadCancel();
31724                     this.hide();
31725                 }
31726             }
31727         });
31728          
31729         this.progressDialog.render(Roo.get(document.body));
31730          
31731         this.progress = new Roo.bootstrap.Progress({
31732             cls : 'roo-document-manager-progress',
31733             active : true,
31734             striped : true
31735         });
31736         
31737         this.progress.render(this.progressDialog.getChildContainer());
31738         
31739         this.progressBar = new Roo.bootstrap.ProgressBar({
31740             cls : 'roo-document-manager-progress-bar',
31741             aria_valuenow : 0,
31742             aria_valuemin : 0,
31743             aria_valuemax : 12,
31744             panel : 'success'
31745         });
31746         
31747         this.progressBar.render(this.progress.getChildContainer());
31748     },
31749     
31750     onUploaderClick : function(e)
31751     {
31752         e.preventDefault();
31753      
31754         if(this.fireEvent('beforeselectfile', this) != false){
31755             this.selectorEl.dom.click();
31756         }
31757         
31758     },
31759     
31760     onFileSelected : function(e)
31761     {
31762         e.preventDefault();
31763         
31764         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31765             return;
31766         }
31767         
31768         Roo.each(this.selectorEl.dom.files, function(file){
31769             if(this.fireEvent('inspect', this, file) != false){
31770                 this.files.push(file);
31771             }
31772         }, this);
31773         
31774         this.queue();
31775         
31776     },
31777     
31778     queue : function()
31779     {
31780         this.selectorEl.dom.value = '';
31781         
31782         if(!this.files || !this.files.length){
31783             return;
31784         }
31785         
31786         if(this.boxes > 0 && this.files.length > this.boxes){
31787             this.files = this.files.slice(0, this.boxes);
31788         }
31789         
31790         this.uploader.show();
31791         
31792         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31793             this.uploader.hide();
31794         }
31795         
31796         var _this = this;
31797         
31798         var files = [];
31799         
31800         var docs = [];
31801         
31802         Roo.each(this.files, function(file){
31803             
31804             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31805                 var f = this.renderPreview(file);
31806                 files.push(f);
31807                 return;
31808             }
31809             
31810             if(file.type.indexOf('image') != -1){
31811                 this.delegates.push(
31812                     (function(){
31813                         _this.process(file);
31814                     }).createDelegate(this)
31815                 );
31816         
31817                 return;
31818             }
31819             
31820             docs.push(
31821                 (function(){
31822                     _this.process(file);
31823                 }).createDelegate(this)
31824             );
31825             
31826         }, this);
31827         
31828         this.files = files;
31829         
31830         this.delegates = this.delegates.concat(docs);
31831         
31832         if(!this.delegates.length){
31833             this.refresh();
31834             return;
31835         }
31836         
31837         this.progressBar.aria_valuemax = this.delegates.length;
31838         
31839         this.arrange();
31840         
31841         return;
31842     },
31843     
31844     arrange : function()
31845     {
31846         if(!this.delegates.length){
31847             this.progressDialog.hide();
31848             this.refresh();
31849             return;
31850         }
31851         
31852         var delegate = this.delegates.shift();
31853         
31854         this.progressDialog.show();
31855         
31856         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31857         
31858         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31859         
31860         delegate();
31861     },
31862     
31863     refresh : function()
31864     {
31865         this.uploader.show();
31866         
31867         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31868             this.uploader.hide();
31869         }
31870         
31871         Roo.isTouch ? this.closable(false) : this.closable(true);
31872         
31873         this.fireEvent('refresh', this);
31874     },
31875     
31876     onRemove : function(e, el, o)
31877     {
31878         e.preventDefault();
31879         
31880         this.fireEvent('remove', this, o);
31881         
31882     },
31883     
31884     remove : function(o)
31885     {
31886         var files = [];
31887         
31888         Roo.each(this.files, function(file){
31889             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31890                 files.push(file);
31891                 return;
31892             }
31893
31894             o.target.remove();
31895
31896         }, this);
31897         
31898         this.files = files;
31899         
31900         this.refresh();
31901     },
31902     
31903     clear : function()
31904     {
31905         Roo.each(this.files, function(file){
31906             if(!file.target){
31907                 return;
31908             }
31909             
31910             file.target.remove();
31911
31912         }, this);
31913         
31914         this.files = [];
31915         
31916         this.refresh();
31917     },
31918     
31919     onClick : function(e, el, o)
31920     {
31921         e.preventDefault();
31922         
31923         this.fireEvent('click', this, o);
31924         
31925     },
31926     
31927     closable : function(closable)
31928     {
31929         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31930             
31931             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31932             
31933             if(closable){
31934                 el.show();
31935                 return;
31936             }
31937             
31938             el.hide();
31939             
31940         }, this);
31941     },
31942     
31943     xhrOnLoad : function(xhr)
31944     {
31945         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31946             el.remove();
31947         }, this);
31948         
31949         if (xhr.readyState !== 4) {
31950             this.arrange();
31951             this.fireEvent('exception', this, xhr);
31952             return;
31953         }
31954
31955         var response = Roo.decode(xhr.responseText);
31956         
31957         if(!response.success){
31958             this.arrange();
31959             this.fireEvent('exception', this, xhr);
31960             return;
31961         }
31962         
31963         var file = this.renderPreview(response.data);
31964         
31965         this.files.push(file);
31966         
31967         this.arrange();
31968         
31969         this.fireEvent('afterupload', this, xhr);
31970         
31971     },
31972     
31973     xhrOnError : function(xhr)
31974     {
31975         Roo.log('xhr on error');
31976         
31977         var response = Roo.decode(xhr.responseText);
31978           
31979         Roo.log(response);
31980         
31981         this.arrange();
31982     },
31983     
31984     process : function(file)
31985     {
31986         if(this.fireEvent('process', this, file) !== false){
31987             if(this.editable && file.type.indexOf('image') != -1){
31988                 this.fireEvent('edit', this, file);
31989                 return;
31990             }
31991
31992             this.uploadStart(file, false);
31993
31994             return;
31995         }
31996         
31997     },
31998     
31999     uploadStart : function(file, crop)
32000     {
32001         this.xhr = new XMLHttpRequest();
32002         
32003         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
32004             this.arrange();
32005             return;
32006         }
32007         
32008         file.xhr = this.xhr;
32009             
32010         this.managerEl.createChild({
32011             tag : 'div',
32012             cls : 'roo-document-manager-loading',
32013             cn : [
32014                 {
32015                     tag : 'div',
32016                     tooltip : file.name,
32017                     cls : 'roo-document-manager-thumb',
32018                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32019                 }
32020             ]
32021
32022         });
32023
32024         this.xhr.open(this.method, this.url, true);
32025         
32026         var headers = {
32027             "Accept": "application/json",
32028             "Cache-Control": "no-cache",
32029             "X-Requested-With": "XMLHttpRequest"
32030         };
32031         
32032         for (var headerName in headers) {
32033             var headerValue = headers[headerName];
32034             if (headerValue) {
32035                 this.xhr.setRequestHeader(headerName, headerValue);
32036             }
32037         }
32038         
32039         var _this = this;
32040         
32041         this.xhr.onload = function()
32042         {
32043             _this.xhrOnLoad(_this.xhr);
32044         }
32045         
32046         this.xhr.onerror = function()
32047         {
32048             _this.xhrOnError(_this.xhr);
32049         }
32050         
32051         var formData = new FormData();
32052
32053         formData.append('returnHTML', 'NO');
32054         
32055         if(crop){
32056             formData.append('crop', crop);
32057         }
32058         
32059         formData.append(this.paramName, file, file.name);
32060         
32061         var options = {
32062             file : file, 
32063             manually : false
32064         };
32065         
32066         if(this.fireEvent('prepare', this, formData, options) != false){
32067             
32068             if(options.manually){
32069                 return;
32070             }
32071             
32072             this.xhr.send(formData);
32073             return;
32074         };
32075         
32076         this.uploadCancel();
32077     },
32078     
32079     uploadCancel : function()
32080     {
32081         if (this.xhr) {
32082             this.xhr.abort();
32083         }
32084         
32085         this.delegates = [];
32086         
32087         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32088             el.remove();
32089         }, this);
32090         
32091         this.arrange();
32092     },
32093     
32094     renderPreview : function(file)
32095     {
32096         if(typeof(file.target) != 'undefined' && file.target){
32097             return file;
32098         }
32099         
32100         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32101         
32102         var previewEl = this.managerEl.createChild({
32103             tag : 'div',
32104             cls : 'roo-document-manager-preview',
32105             cn : [
32106                 {
32107                     tag : 'div',
32108                     tooltip : file[this.toolTipName],
32109                     cls : 'roo-document-manager-thumb',
32110                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32111                 },
32112                 {
32113                     tag : 'button',
32114                     cls : 'close',
32115                     html : '<i class="fa fa-times-circle"></i>'
32116                 }
32117             ]
32118         });
32119
32120         var close = previewEl.select('button.close', true).first();
32121
32122         close.on('click', this.onRemove, this, file);
32123
32124         file.target = previewEl;
32125
32126         var image = previewEl.select('img', true).first();
32127         
32128         var _this = this;
32129         
32130         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32131         
32132         image.on('click', this.onClick, this, file);
32133         
32134         this.fireEvent('previewrendered', this, file);
32135         
32136         return file;
32137         
32138     },
32139     
32140     onPreviewLoad : function(file, image)
32141     {
32142         if(typeof(file.target) == 'undefined' || !file.target){
32143             return;
32144         }
32145         
32146         var width = image.dom.naturalWidth || image.dom.width;
32147         var height = image.dom.naturalHeight || image.dom.height;
32148         
32149         if(!this.previewResize) {
32150             return;
32151         }
32152         
32153         if(width > height){
32154             file.target.addClass('wide');
32155             return;
32156         }
32157         
32158         file.target.addClass('tall');
32159         return;
32160         
32161     },
32162     
32163     uploadFromSource : function(file, crop)
32164     {
32165         this.xhr = new XMLHttpRequest();
32166         
32167         this.managerEl.createChild({
32168             tag : 'div',
32169             cls : 'roo-document-manager-loading',
32170             cn : [
32171                 {
32172                     tag : 'div',
32173                     tooltip : file.name,
32174                     cls : 'roo-document-manager-thumb',
32175                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32176                 }
32177             ]
32178
32179         });
32180
32181         this.xhr.open(this.method, this.url, true);
32182         
32183         var headers = {
32184             "Accept": "application/json",
32185             "Cache-Control": "no-cache",
32186             "X-Requested-With": "XMLHttpRequest"
32187         };
32188         
32189         for (var headerName in headers) {
32190             var headerValue = headers[headerName];
32191             if (headerValue) {
32192                 this.xhr.setRequestHeader(headerName, headerValue);
32193             }
32194         }
32195         
32196         var _this = this;
32197         
32198         this.xhr.onload = function()
32199         {
32200             _this.xhrOnLoad(_this.xhr);
32201         }
32202         
32203         this.xhr.onerror = function()
32204         {
32205             _this.xhrOnError(_this.xhr);
32206         }
32207         
32208         var formData = new FormData();
32209
32210         formData.append('returnHTML', 'NO');
32211         
32212         formData.append('crop', crop);
32213         
32214         if(typeof(file.filename) != 'undefined'){
32215             formData.append('filename', file.filename);
32216         }
32217         
32218         if(typeof(file.mimetype) != 'undefined'){
32219             formData.append('mimetype', file.mimetype);
32220         }
32221         
32222         Roo.log(formData);
32223         
32224         if(this.fireEvent('prepare', this, formData) != false){
32225             this.xhr.send(formData);
32226         };
32227     }
32228 });
32229
32230 /*
32231 * Licence: LGPL
32232 */
32233
32234 /**
32235  * @class Roo.bootstrap.DocumentViewer
32236  * @extends Roo.bootstrap.Component
32237  * Bootstrap DocumentViewer class
32238  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32239  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32240  * 
32241  * @constructor
32242  * Create a new DocumentViewer
32243  * @param {Object} config The config object
32244  */
32245
32246 Roo.bootstrap.DocumentViewer = function(config){
32247     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32248     
32249     this.addEvents({
32250         /**
32251          * @event initial
32252          * Fire after initEvent
32253          * @param {Roo.bootstrap.DocumentViewer} this
32254          */
32255         "initial" : true,
32256         /**
32257          * @event click
32258          * Fire after click
32259          * @param {Roo.bootstrap.DocumentViewer} this
32260          */
32261         "click" : true,
32262         /**
32263          * @event download
32264          * Fire after download button
32265          * @param {Roo.bootstrap.DocumentViewer} this
32266          */
32267         "download" : true,
32268         /**
32269          * @event trash
32270          * Fire after trash button
32271          * @param {Roo.bootstrap.DocumentViewer} this
32272          */
32273         "trash" : true
32274         
32275     });
32276 };
32277
32278 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32279     
32280     showDownload : true,
32281     
32282     showTrash : true,
32283     
32284     getAutoCreate : function()
32285     {
32286         var cfg = {
32287             tag : 'div',
32288             cls : 'roo-document-viewer',
32289             cn : [
32290                 {
32291                     tag : 'div',
32292                     cls : 'roo-document-viewer-body',
32293                     cn : [
32294                         {
32295                             tag : 'div',
32296                             cls : 'roo-document-viewer-thumb',
32297                             cn : [
32298                                 {
32299                                     tag : 'img',
32300                                     cls : 'roo-document-viewer-image'
32301                                 }
32302                             ]
32303                         }
32304                     ]
32305                 },
32306                 {
32307                     tag : 'div',
32308                     cls : 'roo-document-viewer-footer',
32309                     cn : {
32310                         tag : 'div',
32311                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32312                         cn : [
32313                             {
32314                                 tag : 'div',
32315                                 cls : 'btn-group roo-document-viewer-download',
32316                                 cn : [
32317                                     {
32318                                         tag : 'button',
32319                                         cls : 'btn btn-default',
32320                                         html : '<i class="fa fa-download"></i>'
32321                                     }
32322                                 ]
32323                             },
32324                             {
32325                                 tag : 'div',
32326                                 cls : 'btn-group roo-document-viewer-trash',
32327                                 cn : [
32328                                     {
32329                                         tag : 'button',
32330                                         cls : 'btn btn-default',
32331                                         html : '<i class="fa fa-trash"></i>'
32332                                     }
32333                                 ]
32334                             }
32335                         ]
32336                     }
32337                 }
32338             ]
32339         };
32340         
32341         return cfg;
32342     },
32343     
32344     initEvents : function()
32345     {
32346         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32347         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32348         
32349         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32350         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32351         
32352         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32353         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32354         
32355         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32356         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32357         
32358         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32359         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32360         
32361         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32362         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32363         
32364         this.bodyEl.on('click', this.onClick, this);
32365         this.downloadBtn.on('click', this.onDownload, this);
32366         this.trashBtn.on('click', this.onTrash, this);
32367         
32368         this.downloadBtn.hide();
32369         this.trashBtn.hide();
32370         
32371         if(this.showDownload){
32372             this.downloadBtn.show();
32373         }
32374         
32375         if(this.showTrash){
32376             this.trashBtn.show();
32377         }
32378         
32379         if(!this.showDownload && !this.showTrash) {
32380             this.footerEl.hide();
32381         }
32382         
32383     },
32384     
32385     initial : function()
32386     {
32387         this.fireEvent('initial', this);
32388         
32389     },
32390     
32391     onClick : function(e)
32392     {
32393         e.preventDefault();
32394         
32395         this.fireEvent('click', this);
32396     },
32397     
32398     onDownload : function(e)
32399     {
32400         e.preventDefault();
32401         
32402         this.fireEvent('download', this);
32403     },
32404     
32405     onTrash : function(e)
32406     {
32407         e.preventDefault();
32408         
32409         this.fireEvent('trash', this);
32410     }
32411     
32412 });
32413 /*
32414  * - LGPL
32415  *
32416  * nav progress bar
32417  * 
32418  */
32419
32420 /**
32421  * @class Roo.bootstrap.NavProgressBar
32422  * @extends Roo.bootstrap.Component
32423  * Bootstrap NavProgressBar class
32424  * 
32425  * @constructor
32426  * Create a new nav progress bar
32427  * @param {Object} config The config object
32428  */
32429
32430 Roo.bootstrap.NavProgressBar = function(config){
32431     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32432
32433     this.bullets = this.bullets || [];
32434    
32435 //    Roo.bootstrap.NavProgressBar.register(this);
32436      this.addEvents({
32437         /**
32438              * @event changed
32439              * Fires when the active item changes
32440              * @param {Roo.bootstrap.NavProgressBar} this
32441              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32442              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32443          */
32444         'changed': true
32445      });
32446     
32447 };
32448
32449 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32450     
32451     bullets : [],
32452     barItems : [],
32453     
32454     getAutoCreate : function()
32455     {
32456         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32457         
32458         cfg = {
32459             tag : 'div',
32460             cls : 'roo-navigation-bar-group',
32461             cn : [
32462                 {
32463                     tag : 'div',
32464                     cls : 'roo-navigation-top-bar'
32465                 },
32466                 {
32467                     tag : 'div',
32468                     cls : 'roo-navigation-bullets-bar',
32469                     cn : [
32470                         {
32471                             tag : 'ul',
32472                             cls : 'roo-navigation-bar'
32473                         }
32474                     ]
32475                 },
32476                 
32477                 {
32478                     tag : 'div',
32479                     cls : 'roo-navigation-bottom-bar'
32480                 }
32481             ]
32482             
32483         };
32484         
32485         return cfg;
32486         
32487     },
32488     
32489     initEvents: function() 
32490     {
32491         
32492     },
32493     
32494     onRender : function(ct, position) 
32495     {
32496         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32497         
32498         if(this.bullets.length){
32499             Roo.each(this.bullets, function(b){
32500                this.addItem(b);
32501             }, this);
32502         }
32503         
32504         this.format();
32505         
32506     },
32507     
32508     addItem : function(cfg)
32509     {
32510         var item = new Roo.bootstrap.NavProgressItem(cfg);
32511         
32512         item.parentId = this.id;
32513         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32514         
32515         if(cfg.html){
32516             var top = new Roo.bootstrap.Element({
32517                 tag : 'div',
32518                 cls : 'roo-navigation-bar-text'
32519             });
32520             
32521             var bottom = new Roo.bootstrap.Element({
32522                 tag : 'div',
32523                 cls : 'roo-navigation-bar-text'
32524             });
32525             
32526             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32527             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32528             
32529             var topText = new Roo.bootstrap.Element({
32530                 tag : 'span',
32531                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32532             });
32533             
32534             var bottomText = new Roo.bootstrap.Element({
32535                 tag : 'span',
32536                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32537             });
32538             
32539             topText.onRender(top.el, null);
32540             bottomText.onRender(bottom.el, null);
32541             
32542             item.topEl = top;
32543             item.bottomEl = bottom;
32544         }
32545         
32546         this.barItems.push(item);
32547         
32548         return item;
32549     },
32550     
32551     getActive : function()
32552     {
32553         var active = false;
32554         
32555         Roo.each(this.barItems, function(v){
32556             
32557             if (!v.isActive()) {
32558                 return;
32559             }
32560             
32561             active = v;
32562             return false;
32563             
32564         });
32565         
32566         return active;
32567     },
32568     
32569     setActiveItem : function(item)
32570     {
32571         var prev = false;
32572         
32573         Roo.each(this.barItems, function(v){
32574             if (v.rid == item.rid) {
32575                 return ;
32576             }
32577             
32578             if (v.isActive()) {
32579                 v.setActive(false);
32580                 prev = v;
32581             }
32582         });
32583
32584         item.setActive(true);
32585         
32586         this.fireEvent('changed', this, item, prev);
32587     },
32588     
32589     getBarItem: function(rid)
32590     {
32591         var ret = false;
32592         
32593         Roo.each(this.barItems, function(e) {
32594             if (e.rid != rid) {
32595                 return;
32596             }
32597             
32598             ret =  e;
32599             return false;
32600         });
32601         
32602         return ret;
32603     },
32604     
32605     indexOfItem : function(item)
32606     {
32607         var index = false;
32608         
32609         Roo.each(this.barItems, function(v, i){
32610             
32611             if (v.rid != item.rid) {
32612                 return;
32613             }
32614             
32615             index = i;
32616             return false
32617         });
32618         
32619         return index;
32620     },
32621     
32622     setActiveNext : function()
32623     {
32624         var i = this.indexOfItem(this.getActive());
32625         
32626         if (i > this.barItems.length) {
32627             return;
32628         }
32629         
32630         this.setActiveItem(this.barItems[i+1]);
32631     },
32632     
32633     setActivePrev : function()
32634     {
32635         var i = this.indexOfItem(this.getActive());
32636         
32637         if (i  < 1) {
32638             return;
32639         }
32640         
32641         this.setActiveItem(this.barItems[i-1]);
32642     },
32643     
32644     format : function()
32645     {
32646         if(!this.barItems.length){
32647             return;
32648         }
32649      
32650         var width = 100 / this.barItems.length;
32651         
32652         Roo.each(this.barItems, function(i){
32653             i.el.setStyle('width', width + '%');
32654             i.topEl.el.setStyle('width', width + '%');
32655             i.bottomEl.el.setStyle('width', width + '%');
32656         }, this);
32657         
32658     }
32659     
32660 });
32661 /*
32662  * - LGPL
32663  *
32664  * Nav Progress Item
32665  * 
32666  */
32667
32668 /**
32669  * @class Roo.bootstrap.NavProgressItem
32670  * @extends Roo.bootstrap.Component
32671  * Bootstrap NavProgressItem class
32672  * @cfg {String} rid the reference id
32673  * @cfg {Boolean} active (true|false) Is item active default false
32674  * @cfg {Boolean} disabled (true|false) Is item active default false
32675  * @cfg {String} html
32676  * @cfg {String} position (top|bottom) text position default bottom
32677  * @cfg {String} icon show icon instead of number
32678  * 
32679  * @constructor
32680  * Create a new NavProgressItem
32681  * @param {Object} config The config object
32682  */
32683 Roo.bootstrap.NavProgressItem = function(config){
32684     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32685     this.addEvents({
32686         // raw events
32687         /**
32688          * @event click
32689          * The raw click event for the entire grid.
32690          * @param {Roo.bootstrap.NavProgressItem} this
32691          * @param {Roo.EventObject} e
32692          */
32693         "click" : true
32694     });
32695    
32696 };
32697
32698 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32699     
32700     rid : '',
32701     active : false,
32702     disabled : false,
32703     html : '',
32704     position : 'bottom',
32705     icon : false,
32706     
32707     getAutoCreate : function()
32708     {
32709         var iconCls = 'roo-navigation-bar-item-icon';
32710         
32711         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32712         
32713         var cfg = {
32714             tag: 'li',
32715             cls: 'roo-navigation-bar-item',
32716             cn : [
32717                 {
32718                     tag : 'i',
32719                     cls : iconCls
32720                 }
32721             ]
32722         };
32723         
32724         if(this.active){
32725             cfg.cls += ' active';
32726         }
32727         if(this.disabled){
32728             cfg.cls += ' disabled';
32729         }
32730         
32731         return cfg;
32732     },
32733     
32734     disable : function()
32735     {
32736         this.setDisabled(true);
32737     },
32738     
32739     enable : function()
32740     {
32741         this.setDisabled(false);
32742     },
32743     
32744     initEvents: function() 
32745     {
32746         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32747         
32748         this.iconEl.on('click', this.onClick, this);
32749     },
32750     
32751     onClick : function(e)
32752     {
32753         e.preventDefault();
32754         
32755         if(this.disabled){
32756             return;
32757         }
32758         
32759         if(this.fireEvent('click', this, e) === false){
32760             return;
32761         };
32762         
32763         this.parent().setActiveItem(this);
32764     },
32765     
32766     isActive: function () 
32767     {
32768         return this.active;
32769     },
32770     
32771     setActive : function(state)
32772     {
32773         if(this.active == state){
32774             return;
32775         }
32776         
32777         this.active = state;
32778         
32779         if (state) {
32780             this.el.addClass('active');
32781             return;
32782         }
32783         
32784         this.el.removeClass('active');
32785         
32786         return;
32787     },
32788     
32789     setDisabled : function(state)
32790     {
32791         if(this.disabled == state){
32792             return;
32793         }
32794         
32795         this.disabled = state;
32796         
32797         if (state) {
32798             this.el.addClass('disabled');
32799             return;
32800         }
32801         
32802         this.el.removeClass('disabled');
32803     },
32804     
32805     tooltipEl : function()
32806     {
32807         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32808     }
32809 });
32810  
32811
32812  /*
32813  * - LGPL
32814  *
32815  * FieldLabel
32816  * 
32817  */
32818
32819 /**
32820  * @class Roo.bootstrap.FieldLabel
32821  * @extends Roo.bootstrap.Component
32822  * Bootstrap FieldLabel class
32823  * @cfg {String} html contents of the element
32824  * @cfg {String} tag tag of the element default label
32825  * @cfg {String} cls class of the element
32826  * @cfg {String} target label target 
32827  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32828  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32829  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32830  * @cfg {String} iconTooltip default "This field is required"
32831  * @cfg {String} indicatorpos (left|right) default left
32832  * 
32833  * @constructor
32834  * Create a new FieldLabel
32835  * @param {Object} config The config object
32836  */
32837
32838 Roo.bootstrap.FieldLabel = function(config){
32839     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32840     
32841     this.addEvents({
32842             /**
32843              * @event invalid
32844              * Fires after the field has been marked as invalid.
32845              * @param {Roo.form.FieldLabel} this
32846              * @param {String} msg The validation message
32847              */
32848             invalid : true,
32849             /**
32850              * @event valid
32851              * Fires after the field has been validated with no errors.
32852              * @param {Roo.form.FieldLabel} this
32853              */
32854             valid : true
32855         });
32856 };
32857
32858 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32859     
32860     tag: 'label',
32861     cls: '',
32862     html: '',
32863     target: '',
32864     allowBlank : true,
32865     invalidClass : 'has-warning',
32866     validClass : 'has-success',
32867     iconTooltip : 'This field is required',
32868     indicatorpos : 'left',
32869     
32870     getAutoCreate : function(){
32871         
32872         var cls = "";
32873         if (!this.allowBlank) {
32874             cls  = "visible";
32875         }
32876         
32877         var cfg = {
32878             tag : this.tag,
32879             cls : 'roo-bootstrap-field-label ' + this.cls,
32880             for : this.target,
32881             cn : [
32882                 {
32883                     tag : 'i',
32884                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32885                     tooltip : this.iconTooltip
32886                 },
32887                 {
32888                     tag : 'span',
32889                     html : this.html
32890                 }
32891             ] 
32892         };
32893         
32894         if(this.indicatorpos == 'right'){
32895             var cfg = {
32896                 tag : this.tag,
32897                 cls : 'roo-bootstrap-field-label ' + this.cls,
32898                 for : this.target,
32899                 cn : [
32900                     {
32901                         tag : 'span',
32902                         html : this.html
32903                     },
32904                     {
32905                         tag : 'i',
32906                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32907                         tooltip : this.iconTooltip
32908                     }
32909                 ] 
32910             };
32911         }
32912         
32913         return cfg;
32914     },
32915     
32916     initEvents: function() 
32917     {
32918         Roo.bootstrap.Element.superclass.initEvents.call(this);
32919         
32920         this.indicator = this.indicatorEl();
32921         
32922         if(this.indicator){
32923             this.indicator.removeClass('visible');
32924             this.indicator.addClass('invisible');
32925         }
32926         
32927         Roo.bootstrap.FieldLabel.register(this);
32928     },
32929     
32930     indicatorEl : function()
32931     {
32932         var indicator = this.el.select('i.roo-required-indicator',true).first();
32933         
32934         if(!indicator){
32935             return false;
32936         }
32937         
32938         return indicator;
32939         
32940     },
32941     
32942     /**
32943      * Mark this field as valid
32944      */
32945     markValid : function()
32946     {
32947         if(this.indicator){
32948             this.indicator.removeClass('visible');
32949             this.indicator.addClass('invisible');
32950         }
32951         if (Roo.bootstrap.version == 3) {
32952             this.el.removeClass(this.invalidClass);
32953             this.el.addClass(this.validClass);
32954         } else {
32955             this.el.removeClass('is-invalid');
32956             this.el.addClass('is-valid');
32957         }
32958         
32959         
32960         this.fireEvent('valid', this);
32961     },
32962     
32963     /**
32964      * Mark this field as invalid
32965      * @param {String} msg The validation message
32966      */
32967     markInvalid : function(msg)
32968     {
32969         if(this.indicator){
32970             this.indicator.removeClass('invisible');
32971             this.indicator.addClass('visible');
32972         }
32973           if (Roo.bootstrap.version == 3) {
32974             this.el.removeClass(this.validClass);
32975             this.el.addClass(this.invalidClass);
32976         } else {
32977             this.el.removeClass('is-valid');
32978             this.el.addClass('is-invalid');
32979         }
32980         
32981         
32982         this.fireEvent('invalid', this, msg);
32983     }
32984     
32985    
32986 });
32987
32988 Roo.apply(Roo.bootstrap.FieldLabel, {
32989     
32990     groups: {},
32991     
32992      /**
32993     * register a FieldLabel Group
32994     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32995     */
32996     register : function(label)
32997     {
32998         if(this.groups.hasOwnProperty(label.target)){
32999             return;
33000         }
33001      
33002         this.groups[label.target] = label;
33003         
33004     },
33005     /**
33006     * fetch a FieldLabel Group based on the target
33007     * @param {string} target
33008     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
33009     */
33010     get: function(target) {
33011         if (typeof(this.groups[target]) == 'undefined') {
33012             return false;
33013         }
33014         
33015         return this.groups[target] ;
33016     }
33017 });
33018
33019  
33020
33021  /*
33022  * - LGPL
33023  *
33024  * page DateSplitField.
33025  * 
33026  */
33027
33028
33029 /**
33030  * @class Roo.bootstrap.DateSplitField
33031  * @extends Roo.bootstrap.Component
33032  * Bootstrap DateSplitField class
33033  * @cfg {string} fieldLabel - the label associated
33034  * @cfg {Number} labelWidth set the width of label (0-12)
33035  * @cfg {String} labelAlign (top|left)
33036  * @cfg {Boolean} dayAllowBlank (true|false) default false
33037  * @cfg {Boolean} monthAllowBlank (true|false) default false
33038  * @cfg {Boolean} yearAllowBlank (true|false) default false
33039  * @cfg {string} dayPlaceholder 
33040  * @cfg {string} monthPlaceholder
33041  * @cfg {string} yearPlaceholder
33042  * @cfg {string} dayFormat default 'd'
33043  * @cfg {string} monthFormat default 'm'
33044  * @cfg {string} yearFormat default 'Y'
33045  * @cfg {Number} labellg set the width of label (1-12)
33046  * @cfg {Number} labelmd set the width of label (1-12)
33047  * @cfg {Number} labelsm set the width of label (1-12)
33048  * @cfg {Number} labelxs set the width of label (1-12)
33049
33050  *     
33051  * @constructor
33052  * Create a new DateSplitField
33053  * @param {Object} config The config object
33054  */
33055
33056 Roo.bootstrap.DateSplitField = function(config){
33057     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33058     
33059     this.addEvents({
33060         // raw events
33061          /**
33062          * @event years
33063          * getting the data of years
33064          * @param {Roo.bootstrap.DateSplitField} this
33065          * @param {Object} years
33066          */
33067         "years" : true,
33068         /**
33069          * @event days
33070          * getting the data of days
33071          * @param {Roo.bootstrap.DateSplitField} this
33072          * @param {Object} days
33073          */
33074         "days" : true,
33075         /**
33076          * @event invalid
33077          * Fires after the field has been marked as invalid.
33078          * @param {Roo.form.Field} this
33079          * @param {String} msg The validation message
33080          */
33081         invalid : true,
33082        /**
33083          * @event valid
33084          * Fires after the field has been validated with no errors.
33085          * @param {Roo.form.Field} this
33086          */
33087         valid : true
33088     });
33089 };
33090
33091 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33092     
33093     fieldLabel : '',
33094     labelAlign : 'top',
33095     labelWidth : 3,
33096     dayAllowBlank : false,
33097     monthAllowBlank : false,
33098     yearAllowBlank : false,
33099     dayPlaceholder : '',
33100     monthPlaceholder : '',
33101     yearPlaceholder : '',
33102     dayFormat : 'd',
33103     monthFormat : 'm',
33104     yearFormat : 'Y',
33105     isFormField : true,
33106     labellg : 0,
33107     labelmd : 0,
33108     labelsm : 0,
33109     labelxs : 0,
33110     
33111     getAutoCreate : function()
33112     {
33113         var cfg = {
33114             tag : 'div',
33115             cls : 'row roo-date-split-field-group',
33116             cn : [
33117                 {
33118                     tag : 'input',
33119                     type : 'hidden',
33120                     cls : 'form-hidden-field roo-date-split-field-group-value',
33121                     name : this.name
33122                 }
33123             ]
33124         };
33125         
33126         var labelCls = 'col-md-12';
33127         var contentCls = 'col-md-4';
33128         
33129         if(this.fieldLabel){
33130             
33131             var label = {
33132                 tag : 'div',
33133                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33134                 cn : [
33135                     {
33136                         tag : 'label',
33137                         html : this.fieldLabel
33138                     }
33139                 ]
33140             };
33141             
33142             if(this.labelAlign == 'left'){
33143             
33144                 if(this.labelWidth > 12){
33145                     label.style = "width: " + this.labelWidth + 'px';
33146                 }
33147
33148                 if(this.labelWidth < 13 && this.labelmd == 0){
33149                     this.labelmd = this.labelWidth;
33150                 }
33151
33152                 if(this.labellg > 0){
33153                     labelCls = ' col-lg-' + this.labellg;
33154                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33155                 }
33156
33157                 if(this.labelmd > 0){
33158                     labelCls = ' col-md-' + this.labelmd;
33159                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33160                 }
33161
33162                 if(this.labelsm > 0){
33163                     labelCls = ' col-sm-' + this.labelsm;
33164                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33165                 }
33166
33167                 if(this.labelxs > 0){
33168                     labelCls = ' col-xs-' + this.labelxs;
33169                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33170                 }
33171             }
33172             
33173             label.cls += ' ' + labelCls;
33174             
33175             cfg.cn.push(label);
33176         }
33177         
33178         Roo.each(['day', 'month', 'year'], function(t){
33179             cfg.cn.push({
33180                 tag : 'div',
33181                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33182             });
33183         }, this);
33184         
33185         return cfg;
33186     },
33187     
33188     inputEl: function ()
33189     {
33190         return this.el.select('.roo-date-split-field-group-value', true).first();
33191     },
33192     
33193     onRender : function(ct, position) 
33194     {
33195         var _this = this;
33196         
33197         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33198         
33199         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33200         
33201         this.dayField = new Roo.bootstrap.ComboBox({
33202             allowBlank : this.dayAllowBlank,
33203             alwaysQuery : true,
33204             displayField : 'value',
33205             editable : false,
33206             fieldLabel : '',
33207             forceSelection : true,
33208             mode : 'local',
33209             placeholder : this.dayPlaceholder,
33210             selectOnFocus : true,
33211             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33212             triggerAction : 'all',
33213             typeAhead : true,
33214             valueField : 'value',
33215             store : new Roo.data.SimpleStore({
33216                 data : (function() {    
33217                     var days = [];
33218                     _this.fireEvent('days', _this, days);
33219                     return days;
33220                 })(),
33221                 fields : [ 'value' ]
33222             }),
33223             listeners : {
33224                 select : function (_self, record, index)
33225                 {
33226                     _this.setValue(_this.getValue());
33227                 }
33228             }
33229         });
33230
33231         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33232         
33233         this.monthField = new Roo.bootstrap.MonthField({
33234             after : '<i class=\"fa fa-calendar\"></i>',
33235             allowBlank : this.monthAllowBlank,
33236             placeholder : this.monthPlaceholder,
33237             readOnly : true,
33238             listeners : {
33239                 render : function (_self)
33240                 {
33241                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33242                         e.preventDefault();
33243                         _self.focus();
33244                     });
33245                 },
33246                 select : function (_self, oldvalue, newvalue)
33247                 {
33248                     _this.setValue(_this.getValue());
33249                 }
33250             }
33251         });
33252         
33253         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33254         
33255         this.yearField = new Roo.bootstrap.ComboBox({
33256             allowBlank : this.yearAllowBlank,
33257             alwaysQuery : true,
33258             displayField : 'value',
33259             editable : false,
33260             fieldLabel : '',
33261             forceSelection : true,
33262             mode : 'local',
33263             placeholder : this.yearPlaceholder,
33264             selectOnFocus : true,
33265             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33266             triggerAction : 'all',
33267             typeAhead : true,
33268             valueField : 'value',
33269             store : new Roo.data.SimpleStore({
33270                 data : (function() {
33271                     var years = [];
33272                     _this.fireEvent('years', _this, years);
33273                     return years;
33274                 })(),
33275                 fields : [ 'value' ]
33276             }),
33277             listeners : {
33278                 select : function (_self, record, index)
33279                 {
33280                     _this.setValue(_this.getValue());
33281                 }
33282             }
33283         });
33284
33285         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33286     },
33287     
33288     setValue : function(v, format)
33289     {
33290         this.inputEl.dom.value = v;
33291         
33292         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33293         
33294         var d = Date.parseDate(v, f);
33295         
33296         if(!d){
33297             this.validate();
33298             return;
33299         }
33300         
33301         this.setDay(d.format(this.dayFormat));
33302         this.setMonth(d.format(this.monthFormat));
33303         this.setYear(d.format(this.yearFormat));
33304         
33305         this.validate();
33306         
33307         return;
33308     },
33309     
33310     setDay : function(v)
33311     {
33312         this.dayField.setValue(v);
33313         this.inputEl.dom.value = this.getValue();
33314         this.validate();
33315         return;
33316     },
33317     
33318     setMonth : function(v)
33319     {
33320         this.monthField.setValue(v, true);
33321         this.inputEl.dom.value = this.getValue();
33322         this.validate();
33323         return;
33324     },
33325     
33326     setYear : function(v)
33327     {
33328         this.yearField.setValue(v);
33329         this.inputEl.dom.value = this.getValue();
33330         this.validate();
33331         return;
33332     },
33333     
33334     getDay : function()
33335     {
33336         return this.dayField.getValue();
33337     },
33338     
33339     getMonth : function()
33340     {
33341         return this.monthField.getValue();
33342     },
33343     
33344     getYear : function()
33345     {
33346         return this.yearField.getValue();
33347     },
33348     
33349     getValue : function()
33350     {
33351         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33352         
33353         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33354         
33355         return date;
33356     },
33357     
33358     reset : function()
33359     {
33360         this.setDay('');
33361         this.setMonth('');
33362         this.setYear('');
33363         this.inputEl.dom.value = '';
33364         this.validate();
33365         return;
33366     },
33367     
33368     validate : function()
33369     {
33370         var d = this.dayField.validate();
33371         var m = this.monthField.validate();
33372         var y = this.yearField.validate();
33373         
33374         var valid = true;
33375         
33376         if(
33377                 (!this.dayAllowBlank && !d) ||
33378                 (!this.monthAllowBlank && !m) ||
33379                 (!this.yearAllowBlank && !y)
33380         ){
33381             valid = false;
33382         }
33383         
33384         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33385             return valid;
33386         }
33387         
33388         if(valid){
33389             this.markValid();
33390             return valid;
33391         }
33392         
33393         this.markInvalid();
33394         
33395         return valid;
33396     },
33397     
33398     markValid : function()
33399     {
33400         
33401         var label = this.el.select('label', true).first();
33402         var icon = this.el.select('i.fa-star', true).first();
33403
33404         if(label && icon){
33405             icon.remove();
33406         }
33407         
33408         this.fireEvent('valid', this);
33409     },
33410     
33411      /**
33412      * Mark this field as invalid
33413      * @param {String} msg The validation message
33414      */
33415     markInvalid : function(msg)
33416     {
33417         
33418         var label = this.el.select('label', true).first();
33419         var icon = this.el.select('i.fa-star', true).first();
33420
33421         if(label && !icon){
33422             this.el.select('.roo-date-split-field-label', true).createChild({
33423                 tag : 'i',
33424                 cls : 'text-danger fa fa-lg fa-star',
33425                 tooltip : 'This field is required',
33426                 style : 'margin-right:5px;'
33427             }, label, true);
33428         }
33429         
33430         this.fireEvent('invalid', this, msg);
33431     },
33432     
33433     clearInvalid : function()
33434     {
33435         var label = this.el.select('label', true).first();
33436         var icon = this.el.select('i.fa-star', true).first();
33437
33438         if(label && icon){
33439             icon.remove();
33440         }
33441         
33442         this.fireEvent('valid', this);
33443     },
33444     
33445     getName: function()
33446     {
33447         return this.name;
33448     }
33449     
33450 });
33451
33452  /**
33453  *
33454  * This is based on 
33455  * http://masonry.desandro.com
33456  *
33457  * The idea is to render all the bricks based on vertical width...
33458  *
33459  * The original code extends 'outlayer' - we might need to use that....
33460  * 
33461  */
33462
33463
33464 /**
33465  * @class Roo.bootstrap.LayoutMasonry
33466  * @extends Roo.bootstrap.Component
33467  * Bootstrap Layout Masonry class
33468  * 
33469  * @constructor
33470  * Create a new Element
33471  * @param {Object} config The config object
33472  */
33473
33474 Roo.bootstrap.LayoutMasonry = function(config){
33475     
33476     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33477     
33478     this.bricks = [];
33479     
33480     Roo.bootstrap.LayoutMasonry.register(this);
33481     
33482     this.addEvents({
33483         // raw events
33484         /**
33485          * @event layout
33486          * Fire after layout the items
33487          * @param {Roo.bootstrap.LayoutMasonry} this
33488          * @param {Roo.EventObject} e
33489          */
33490         "layout" : true
33491     });
33492     
33493 };
33494
33495 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33496     
33497     /**
33498      * @cfg {Boolean} isLayoutInstant = no animation?
33499      */   
33500     isLayoutInstant : false, // needed?
33501    
33502     /**
33503      * @cfg {Number} boxWidth  width of the columns
33504      */   
33505     boxWidth : 450,
33506     
33507       /**
33508      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33509      */   
33510     boxHeight : 0,
33511     
33512     /**
33513      * @cfg {Number} padWidth padding below box..
33514      */   
33515     padWidth : 10, 
33516     
33517     /**
33518      * @cfg {Number} gutter gutter width..
33519      */   
33520     gutter : 10,
33521     
33522      /**
33523      * @cfg {Number} maxCols maximum number of columns
33524      */   
33525     
33526     maxCols: 0,
33527     
33528     /**
33529      * @cfg {Boolean} isAutoInitial defalut true
33530      */   
33531     isAutoInitial : true, 
33532     
33533     containerWidth: 0,
33534     
33535     /**
33536      * @cfg {Boolean} isHorizontal defalut false
33537      */   
33538     isHorizontal : false, 
33539
33540     currentSize : null,
33541     
33542     tag: 'div',
33543     
33544     cls: '',
33545     
33546     bricks: null, //CompositeElement
33547     
33548     cols : 1,
33549     
33550     _isLayoutInited : false,
33551     
33552 //    isAlternative : false, // only use for vertical layout...
33553     
33554     /**
33555      * @cfg {Number} alternativePadWidth padding below box..
33556      */   
33557     alternativePadWidth : 50,
33558     
33559     selectedBrick : [],
33560     
33561     getAutoCreate : function(){
33562         
33563         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33564         
33565         var cfg = {
33566             tag: this.tag,
33567             cls: 'blog-masonary-wrapper ' + this.cls,
33568             cn : {
33569                 cls : 'mas-boxes masonary'
33570             }
33571         };
33572         
33573         return cfg;
33574     },
33575     
33576     getChildContainer: function( )
33577     {
33578         if (this.boxesEl) {
33579             return this.boxesEl;
33580         }
33581         
33582         this.boxesEl = this.el.select('.mas-boxes').first();
33583         
33584         return this.boxesEl;
33585     },
33586     
33587     
33588     initEvents : function()
33589     {
33590         var _this = this;
33591         
33592         if(this.isAutoInitial){
33593             Roo.log('hook children rendered');
33594             this.on('childrenrendered', function() {
33595                 Roo.log('children rendered');
33596                 _this.initial();
33597             } ,this);
33598         }
33599     },
33600     
33601     initial : function()
33602     {
33603         this.selectedBrick = [];
33604         
33605         this.currentSize = this.el.getBox(true);
33606         
33607         Roo.EventManager.onWindowResize(this.resize, this); 
33608
33609         if(!this.isAutoInitial){
33610             this.layout();
33611             return;
33612         }
33613         
33614         this.layout();
33615         
33616         return;
33617         //this.layout.defer(500,this);
33618         
33619     },
33620     
33621     resize : function()
33622     {
33623         var cs = this.el.getBox(true);
33624         
33625         if (
33626                 this.currentSize.width == cs.width && 
33627                 this.currentSize.x == cs.x && 
33628                 this.currentSize.height == cs.height && 
33629                 this.currentSize.y == cs.y 
33630         ) {
33631             Roo.log("no change in with or X or Y");
33632             return;
33633         }
33634         
33635         this.currentSize = cs;
33636         
33637         this.layout();
33638         
33639     },
33640     
33641     layout : function()
33642     {   
33643         this._resetLayout();
33644         
33645         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33646         
33647         this.layoutItems( isInstant );
33648       
33649         this._isLayoutInited = true;
33650         
33651         this.fireEvent('layout', this);
33652         
33653     },
33654     
33655     _resetLayout : function()
33656     {
33657         if(this.isHorizontal){
33658             this.horizontalMeasureColumns();
33659             return;
33660         }
33661         
33662         this.verticalMeasureColumns();
33663         
33664     },
33665     
33666     verticalMeasureColumns : function()
33667     {
33668         this.getContainerWidth();
33669         
33670 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33671 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33672 //            return;
33673 //        }
33674         
33675         var boxWidth = this.boxWidth + this.padWidth;
33676         
33677         if(this.containerWidth < this.boxWidth){
33678             boxWidth = this.containerWidth
33679         }
33680         
33681         var containerWidth = this.containerWidth;
33682         
33683         var cols = Math.floor(containerWidth / boxWidth);
33684         
33685         this.cols = Math.max( cols, 1 );
33686         
33687         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33688         
33689         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33690         
33691         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33692         
33693         this.colWidth = boxWidth + avail - this.padWidth;
33694         
33695         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33696         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33697     },
33698     
33699     horizontalMeasureColumns : function()
33700     {
33701         this.getContainerWidth();
33702         
33703         var boxWidth = this.boxWidth;
33704         
33705         if(this.containerWidth < boxWidth){
33706             boxWidth = this.containerWidth;
33707         }
33708         
33709         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33710         
33711         this.el.setHeight(boxWidth);
33712         
33713     },
33714     
33715     getContainerWidth : function()
33716     {
33717         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33718     },
33719     
33720     layoutItems : function( isInstant )
33721     {
33722         Roo.log(this.bricks);
33723         
33724         var items = Roo.apply([], this.bricks);
33725         
33726         if(this.isHorizontal){
33727             this._horizontalLayoutItems( items , isInstant );
33728             return;
33729         }
33730         
33731 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33732 //            this._verticalAlternativeLayoutItems( items , isInstant );
33733 //            return;
33734 //        }
33735         
33736         this._verticalLayoutItems( items , isInstant );
33737         
33738     },
33739     
33740     _verticalLayoutItems : function ( items , isInstant)
33741     {
33742         if ( !items || !items.length ) {
33743             return;
33744         }
33745         
33746         var standard = [
33747             ['xs', 'xs', 'xs', 'tall'],
33748             ['xs', 'xs', 'tall'],
33749             ['xs', 'xs', 'sm'],
33750             ['xs', 'xs', 'xs'],
33751             ['xs', 'tall'],
33752             ['xs', 'sm'],
33753             ['xs', 'xs'],
33754             ['xs'],
33755             
33756             ['sm', 'xs', 'xs'],
33757             ['sm', 'xs'],
33758             ['sm'],
33759             
33760             ['tall', 'xs', 'xs', 'xs'],
33761             ['tall', 'xs', 'xs'],
33762             ['tall', 'xs'],
33763             ['tall']
33764             
33765         ];
33766         
33767         var queue = [];
33768         
33769         var boxes = [];
33770         
33771         var box = [];
33772         
33773         Roo.each(items, function(item, k){
33774             
33775             switch (item.size) {
33776                 // these layouts take up a full box,
33777                 case 'md' :
33778                 case 'md-left' :
33779                 case 'md-right' :
33780                 case 'wide' :
33781                     
33782                     if(box.length){
33783                         boxes.push(box);
33784                         box = [];
33785                     }
33786                     
33787                     boxes.push([item]);
33788                     
33789                     break;
33790                     
33791                 case 'xs' :
33792                 case 'sm' :
33793                 case 'tall' :
33794                     
33795                     box.push(item);
33796                     
33797                     break;
33798                 default :
33799                     break;
33800                     
33801             }
33802             
33803         }, this);
33804         
33805         if(box.length){
33806             boxes.push(box);
33807             box = [];
33808         }
33809         
33810         var filterPattern = function(box, length)
33811         {
33812             if(!box.length){
33813                 return;
33814             }
33815             
33816             var match = false;
33817             
33818             var pattern = box.slice(0, length);
33819             
33820             var format = [];
33821             
33822             Roo.each(pattern, function(i){
33823                 format.push(i.size);
33824             }, this);
33825             
33826             Roo.each(standard, function(s){
33827                 
33828                 if(String(s) != String(format)){
33829                     return;
33830                 }
33831                 
33832                 match = true;
33833                 return false;
33834                 
33835             }, this);
33836             
33837             if(!match && length == 1){
33838                 return;
33839             }
33840             
33841             if(!match){
33842                 filterPattern(box, length - 1);
33843                 return;
33844             }
33845                 
33846             queue.push(pattern);
33847
33848             box = box.slice(length, box.length);
33849
33850             filterPattern(box, 4);
33851
33852             return;
33853             
33854         }
33855         
33856         Roo.each(boxes, function(box, k){
33857             
33858             if(!box.length){
33859                 return;
33860             }
33861             
33862             if(box.length == 1){
33863                 queue.push(box);
33864                 return;
33865             }
33866             
33867             filterPattern(box, 4);
33868             
33869         }, this);
33870         
33871         this._processVerticalLayoutQueue( queue, isInstant );
33872         
33873     },
33874     
33875 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33876 //    {
33877 //        if ( !items || !items.length ) {
33878 //            return;
33879 //        }
33880 //
33881 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33882 //        
33883 //    },
33884     
33885     _horizontalLayoutItems : function ( items , isInstant)
33886     {
33887         if ( !items || !items.length || items.length < 3) {
33888             return;
33889         }
33890         
33891         items.reverse();
33892         
33893         var eItems = items.slice(0, 3);
33894         
33895         items = items.slice(3, items.length);
33896         
33897         var standard = [
33898             ['xs', 'xs', 'xs', 'wide'],
33899             ['xs', 'xs', 'wide'],
33900             ['xs', 'xs', 'sm'],
33901             ['xs', 'xs', 'xs'],
33902             ['xs', 'wide'],
33903             ['xs', 'sm'],
33904             ['xs', 'xs'],
33905             ['xs'],
33906             
33907             ['sm', 'xs', 'xs'],
33908             ['sm', 'xs'],
33909             ['sm'],
33910             
33911             ['wide', 'xs', 'xs', 'xs'],
33912             ['wide', 'xs', 'xs'],
33913             ['wide', 'xs'],
33914             ['wide'],
33915             
33916             ['wide-thin']
33917         ];
33918         
33919         var queue = [];
33920         
33921         var boxes = [];
33922         
33923         var box = [];
33924         
33925         Roo.each(items, function(item, k){
33926             
33927             switch (item.size) {
33928                 case 'md' :
33929                 case 'md-left' :
33930                 case 'md-right' :
33931                 case 'tall' :
33932                     
33933                     if(box.length){
33934                         boxes.push(box);
33935                         box = [];
33936                     }
33937                     
33938                     boxes.push([item]);
33939                     
33940                     break;
33941                     
33942                 case 'xs' :
33943                 case 'sm' :
33944                 case 'wide' :
33945                 case 'wide-thin' :
33946                     
33947                     box.push(item);
33948                     
33949                     break;
33950                 default :
33951                     break;
33952                     
33953             }
33954             
33955         }, this);
33956         
33957         if(box.length){
33958             boxes.push(box);
33959             box = [];
33960         }
33961         
33962         var filterPattern = function(box, length)
33963         {
33964             if(!box.length){
33965                 return;
33966             }
33967             
33968             var match = false;
33969             
33970             var pattern = box.slice(0, length);
33971             
33972             var format = [];
33973             
33974             Roo.each(pattern, function(i){
33975                 format.push(i.size);
33976             }, this);
33977             
33978             Roo.each(standard, function(s){
33979                 
33980                 if(String(s) != String(format)){
33981                     return;
33982                 }
33983                 
33984                 match = true;
33985                 return false;
33986                 
33987             }, this);
33988             
33989             if(!match && length == 1){
33990                 return;
33991             }
33992             
33993             if(!match){
33994                 filterPattern(box, length - 1);
33995                 return;
33996             }
33997                 
33998             queue.push(pattern);
33999
34000             box = box.slice(length, box.length);
34001
34002             filterPattern(box, 4);
34003
34004             return;
34005             
34006         }
34007         
34008         Roo.each(boxes, function(box, k){
34009             
34010             if(!box.length){
34011                 return;
34012             }
34013             
34014             if(box.length == 1){
34015                 queue.push(box);
34016                 return;
34017             }
34018             
34019             filterPattern(box, 4);
34020             
34021         }, this);
34022         
34023         
34024         var prune = [];
34025         
34026         var pos = this.el.getBox(true);
34027         
34028         var minX = pos.x;
34029         
34030         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34031         
34032         var hit_end = false;
34033         
34034         Roo.each(queue, function(box){
34035             
34036             if(hit_end){
34037                 
34038                 Roo.each(box, function(b){
34039                 
34040                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34041                     b.el.hide();
34042
34043                 }, this);
34044
34045                 return;
34046             }
34047             
34048             var mx = 0;
34049             
34050             Roo.each(box, function(b){
34051                 
34052                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34053                 b.el.show();
34054
34055                 mx = Math.max(mx, b.x);
34056                 
34057             }, this);
34058             
34059             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34060             
34061             if(maxX < minX){
34062                 
34063                 Roo.each(box, function(b){
34064                 
34065                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34066                     b.el.hide();
34067                     
34068                 }, this);
34069                 
34070                 hit_end = true;
34071                 
34072                 return;
34073             }
34074             
34075             prune.push(box);
34076             
34077         }, this);
34078         
34079         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34080     },
34081     
34082     /** Sets position of item in DOM
34083     * @param {Element} item
34084     * @param {Number} x - horizontal position
34085     * @param {Number} y - vertical position
34086     * @param {Boolean} isInstant - disables transitions
34087     */
34088     _processVerticalLayoutQueue : function( queue, isInstant )
34089     {
34090         var pos = this.el.getBox(true);
34091         var x = pos.x;
34092         var y = pos.y;
34093         var maxY = [];
34094         
34095         for (var i = 0; i < this.cols; i++){
34096             maxY[i] = pos.y;
34097         }
34098         
34099         Roo.each(queue, function(box, k){
34100             
34101             var col = k % this.cols;
34102             
34103             Roo.each(box, function(b,kk){
34104                 
34105                 b.el.position('absolute');
34106                 
34107                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34108                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34109                 
34110                 if(b.size == 'md-left' || b.size == 'md-right'){
34111                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34112                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34113                 }
34114                 
34115                 b.el.setWidth(width);
34116                 b.el.setHeight(height);
34117                 // iframe?
34118                 b.el.select('iframe',true).setSize(width,height);
34119                 
34120             }, this);
34121             
34122             for (var i = 0; i < this.cols; i++){
34123                 
34124                 if(maxY[i] < maxY[col]){
34125                     col = i;
34126                     continue;
34127                 }
34128                 
34129                 col = Math.min(col, i);
34130                 
34131             }
34132             
34133             x = pos.x + col * (this.colWidth + this.padWidth);
34134             
34135             y = maxY[col];
34136             
34137             var positions = [];
34138             
34139             switch (box.length){
34140                 case 1 :
34141                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34142                     break;
34143                 case 2 :
34144                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34145                     break;
34146                 case 3 :
34147                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34148                     break;
34149                 case 4 :
34150                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34151                     break;
34152                 default :
34153                     break;
34154             }
34155             
34156             Roo.each(box, function(b,kk){
34157                 
34158                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34159                 
34160                 var sz = b.el.getSize();
34161                 
34162                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34163                 
34164             }, this);
34165             
34166         }, this);
34167         
34168         var mY = 0;
34169         
34170         for (var i = 0; i < this.cols; i++){
34171             mY = Math.max(mY, maxY[i]);
34172         }
34173         
34174         this.el.setHeight(mY - pos.y);
34175         
34176     },
34177     
34178 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34179 //    {
34180 //        var pos = this.el.getBox(true);
34181 //        var x = pos.x;
34182 //        var y = pos.y;
34183 //        var maxX = pos.right;
34184 //        
34185 //        var maxHeight = 0;
34186 //        
34187 //        Roo.each(items, function(item, k){
34188 //            
34189 //            var c = k % 2;
34190 //            
34191 //            item.el.position('absolute');
34192 //                
34193 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34194 //
34195 //            item.el.setWidth(width);
34196 //
34197 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34198 //
34199 //            item.el.setHeight(height);
34200 //            
34201 //            if(c == 0){
34202 //                item.el.setXY([x, y], isInstant ? false : true);
34203 //            } else {
34204 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34205 //            }
34206 //            
34207 //            y = y + height + this.alternativePadWidth;
34208 //            
34209 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34210 //            
34211 //        }, this);
34212 //        
34213 //        this.el.setHeight(maxHeight);
34214 //        
34215 //    },
34216     
34217     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34218     {
34219         var pos = this.el.getBox(true);
34220         
34221         var minX = pos.x;
34222         var minY = pos.y;
34223         
34224         var maxX = pos.right;
34225         
34226         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34227         
34228         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34229         
34230         Roo.each(queue, function(box, k){
34231             
34232             Roo.each(box, function(b, kk){
34233                 
34234                 b.el.position('absolute');
34235                 
34236                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34237                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34238                 
34239                 if(b.size == 'md-left' || b.size == 'md-right'){
34240                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34241                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34242                 }
34243                 
34244                 b.el.setWidth(width);
34245                 b.el.setHeight(height);
34246                 
34247             }, this);
34248             
34249             if(!box.length){
34250                 return;
34251             }
34252             
34253             var positions = [];
34254             
34255             switch (box.length){
34256                 case 1 :
34257                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34258                     break;
34259                 case 2 :
34260                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34261                     break;
34262                 case 3 :
34263                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34264                     break;
34265                 case 4 :
34266                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34267                     break;
34268                 default :
34269                     break;
34270             }
34271             
34272             Roo.each(box, function(b,kk){
34273                 
34274                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34275                 
34276                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34277                 
34278             }, this);
34279             
34280         }, this);
34281         
34282     },
34283     
34284     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34285     {
34286         Roo.each(eItems, function(b,k){
34287             
34288             b.size = (k == 0) ? 'sm' : 'xs';
34289             b.x = (k == 0) ? 2 : 1;
34290             b.y = (k == 0) ? 2 : 1;
34291             
34292             b.el.position('absolute');
34293             
34294             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34295                 
34296             b.el.setWidth(width);
34297             
34298             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34299             
34300             b.el.setHeight(height);
34301             
34302         }, this);
34303
34304         var positions = [];
34305         
34306         positions.push({
34307             x : maxX - this.unitWidth * 2 - this.gutter,
34308             y : minY
34309         });
34310         
34311         positions.push({
34312             x : maxX - this.unitWidth,
34313             y : minY + (this.unitWidth + this.gutter) * 2
34314         });
34315         
34316         positions.push({
34317             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34318             y : minY
34319         });
34320         
34321         Roo.each(eItems, function(b,k){
34322             
34323             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34324
34325         }, this);
34326         
34327     },
34328     
34329     getVerticalOneBoxColPositions : function(x, y, box)
34330     {
34331         var pos = [];
34332         
34333         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34334         
34335         if(box[0].size == 'md-left'){
34336             rand = 0;
34337         }
34338         
34339         if(box[0].size == 'md-right'){
34340             rand = 1;
34341         }
34342         
34343         pos.push({
34344             x : x + (this.unitWidth + this.gutter) * rand,
34345             y : y
34346         });
34347         
34348         return pos;
34349     },
34350     
34351     getVerticalTwoBoxColPositions : function(x, y, box)
34352     {
34353         var pos = [];
34354         
34355         if(box[0].size == 'xs'){
34356             
34357             pos.push({
34358                 x : x,
34359                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34360             });
34361
34362             pos.push({
34363                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34364                 y : y
34365             });
34366             
34367             return pos;
34368             
34369         }
34370         
34371         pos.push({
34372             x : x,
34373             y : y
34374         });
34375
34376         pos.push({
34377             x : x + (this.unitWidth + this.gutter) * 2,
34378             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34379         });
34380         
34381         return pos;
34382         
34383     },
34384     
34385     getVerticalThreeBoxColPositions : function(x, y, box)
34386     {
34387         var pos = [];
34388         
34389         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34390             
34391             pos.push({
34392                 x : x,
34393                 y : y
34394             });
34395
34396             pos.push({
34397                 x : x + (this.unitWidth + this.gutter) * 1,
34398                 y : y
34399             });
34400             
34401             pos.push({
34402                 x : x + (this.unitWidth + this.gutter) * 2,
34403                 y : y
34404             });
34405             
34406             return pos;
34407             
34408         }
34409         
34410         if(box[0].size == 'xs' && box[1].size == 'xs'){
34411             
34412             pos.push({
34413                 x : x,
34414                 y : y
34415             });
34416
34417             pos.push({
34418                 x : x,
34419                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34420             });
34421             
34422             pos.push({
34423                 x : x + (this.unitWidth + this.gutter) * 1,
34424                 y : y
34425             });
34426             
34427             return pos;
34428             
34429         }
34430         
34431         pos.push({
34432             x : x,
34433             y : y
34434         });
34435
34436         pos.push({
34437             x : x + (this.unitWidth + this.gutter) * 2,
34438             y : y
34439         });
34440
34441         pos.push({
34442             x : x + (this.unitWidth + this.gutter) * 2,
34443             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34444         });
34445             
34446         return pos;
34447         
34448     },
34449     
34450     getVerticalFourBoxColPositions : function(x, y, box)
34451     {
34452         var pos = [];
34453         
34454         if(box[0].size == 'xs'){
34455             
34456             pos.push({
34457                 x : x,
34458                 y : y
34459             });
34460
34461             pos.push({
34462                 x : x,
34463                 y : y + (this.unitHeight + this.gutter) * 1
34464             });
34465             
34466             pos.push({
34467                 x : x,
34468                 y : y + (this.unitHeight + this.gutter) * 2
34469             });
34470             
34471             pos.push({
34472                 x : x + (this.unitWidth + this.gutter) * 1,
34473                 y : y
34474             });
34475             
34476             return pos;
34477             
34478         }
34479         
34480         pos.push({
34481             x : x,
34482             y : y
34483         });
34484
34485         pos.push({
34486             x : x + (this.unitWidth + this.gutter) * 2,
34487             y : y
34488         });
34489
34490         pos.push({
34491             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34492             y : y + (this.unitHeight + this.gutter) * 1
34493         });
34494
34495         pos.push({
34496             x : x + (this.unitWidth + this.gutter) * 2,
34497             y : y + (this.unitWidth + this.gutter) * 2
34498         });
34499
34500         return pos;
34501         
34502     },
34503     
34504     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34505     {
34506         var pos = [];
34507         
34508         if(box[0].size == 'md-left'){
34509             pos.push({
34510                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34511                 y : minY
34512             });
34513             
34514             return pos;
34515         }
34516         
34517         if(box[0].size == 'md-right'){
34518             pos.push({
34519                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34520                 y : minY + (this.unitWidth + this.gutter) * 1
34521             });
34522             
34523             return pos;
34524         }
34525         
34526         var rand = Math.floor(Math.random() * (4 - box[0].y));
34527         
34528         pos.push({
34529             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34530             y : minY + (this.unitWidth + this.gutter) * rand
34531         });
34532         
34533         return pos;
34534         
34535     },
34536     
34537     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34538     {
34539         var pos = [];
34540         
34541         if(box[0].size == 'xs'){
34542             
34543             pos.push({
34544                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34545                 y : minY
34546             });
34547
34548             pos.push({
34549                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34550                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34551             });
34552             
34553             return pos;
34554             
34555         }
34556         
34557         pos.push({
34558             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34559             y : minY
34560         });
34561
34562         pos.push({
34563             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34564             y : minY + (this.unitWidth + this.gutter) * 2
34565         });
34566         
34567         return pos;
34568         
34569     },
34570     
34571     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34572     {
34573         var pos = [];
34574         
34575         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34576             
34577             pos.push({
34578                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34579                 y : minY
34580             });
34581
34582             pos.push({
34583                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34584                 y : minY + (this.unitWidth + this.gutter) * 1
34585             });
34586             
34587             pos.push({
34588                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34589                 y : minY + (this.unitWidth + this.gutter) * 2
34590             });
34591             
34592             return pos;
34593             
34594         }
34595         
34596         if(box[0].size == 'xs' && box[1].size == 'xs'){
34597             
34598             pos.push({
34599                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34600                 y : minY
34601             });
34602
34603             pos.push({
34604                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34605                 y : minY
34606             });
34607             
34608             pos.push({
34609                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34610                 y : minY + (this.unitWidth + this.gutter) * 1
34611             });
34612             
34613             return pos;
34614             
34615         }
34616         
34617         pos.push({
34618             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34619             y : minY
34620         });
34621
34622         pos.push({
34623             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34624             y : minY + (this.unitWidth + this.gutter) * 2
34625         });
34626
34627         pos.push({
34628             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34629             y : minY + (this.unitWidth + this.gutter) * 2
34630         });
34631             
34632         return pos;
34633         
34634     },
34635     
34636     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34637     {
34638         var pos = [];
34639         
34640         if(box[0].size == 'xs'){
34641             
34642             pos.push({
34643                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34644                 y : minY
34645             });
34646
34647             pos.push({
34648                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34649                 y : minY
34650             });
34651             
34652             pos.push({
34653                 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),
34654                 y : minY
34655             });
34656             
34657             pos.push({
34658                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34659                 y : minY + (this.unitWidth + this.gutter) * 1
34660             });
34661             
34662             return pos;
34663             
34664         }
34665         
34666         pos.push({
34667             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34668             y : minY
34669         });
34670         
34671         pos.push({
34672             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34673             y : minY + (this.unitWidth + this.gutter) * 2
34674         });
34675         
34676         pos.push({
34677             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34678             y : minY + (this.unitWidth + this.gutter) * 2
34679         });
34680         
34681         pos.push({
34682             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),
34683             y : minY + (this.unitWidth + this.gutter) * 2
34684         });
34685
34686         return pos;
34687         
34688     },
34689     
34690     /**
34691     * remove a Masonry Brick
34692     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34693     */
34694     removeBrick : function(brick_id)
34695     {
34696         if (!brick_id) {
34697             return;
34698         }
34699         
34700         for (var i = 0; i<this.bricks.length; i++) {
34701             if (this.bricks[i].id == brick_id) {
34702                 this.bricks.splice(i,1);
34703                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34704                 this.initial();
34705             }
34706         }
34707     },
34708     
34709     /**
34710     * adds a Masonry Brick
34711     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34712     */
34713     addBrick : function(cfg)
34714     {
34715         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34716         //this.register(cn);
34717         cn.parentId = this.id;
34718         cn.render(this.el);
34719         return cn;
34720     },
34721     
34722     /**
34723     * register a Masonry Brick
34724     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34725     */
34726     
34727     register : function(brick)
34728     {
34729         this.bricks.push(brick);
34730         brick.masonryId = this.id;
34731     },
34732     
34733     /**
34734     * clear all the Masonry Brick
34735     */
34736     clearAll : function()
34737     {
34738         this.bricks = [];
34739         //this.getChildContainer().dom.innerHTML = "";
34740         this.el.dom.innerHTML = '';
34741     },
34742     
34743     getSelected : function()
34744     {
34745         if (!this.selectedBrick) {
34746             return false;
34747         }
34748         
34749         return this.selectedBrick;
34750     }
34751 });
34752
34753 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34754     
34755     groups: {},
34756      /**
34757     * register a Masonry Layout
34758     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34759     */
34760     
34761     register : function(layout)
34762     {
34763         this.groups[layout.id] = layout;
34764     },
34765     /**
34766     * fetch a  Masonry Layout based on the masonry layout ID
34767     * @param {string} the masonry layout to add
34768     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34769     */
34770     
34771     get: function(layout_id) {
34772         if (typeof(this.groups[layout_id]) == 'undefined') {
34773             return false;
34774         }
34775         return this.groups[layout_id] ;
34776     }
34777     
34778     
34779     
34780 });
34781
34782  
34783
34784  /**
34785  *
34786  * This is based on 
34787  * http://masonry.desandro.com
34788  *
34789  * The idea is to render all the bricks based on vertical width...
34790  *
34791  * The original code extends 'outlayer' - we might need to use that....
34792  * 
34793  */
34794
34795
34796 /**
34797  * @class Roo.bootstrap.LayoutMasonryAuto
34798  * @extends Roo.bootstrap.Component
34799  * Bootstrap Layout Masonry class
34800  * 
34801  * @constructor
34802  * Create a new Element
34803  * @param {Object} config The config object
34804  */
34805
34806 Roo.bootstrap.LayoutMasonryAuto = function(config){
34807     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34808 };
34809
34810 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34811     
34812       /**
34813      * @cfg {Boolean} isFitWidth  - resize the width..
34814      */   
34815     isFitWidth : false,  // options..
34816     /**
34817      * @cfg {Boolean} isOriginLeft = left align?
34818      */   
34819     isOriginLeft : true,
34820     /**
34821      * @cfg {Boolean} isOriginTop = top align?
34822      */   
34823     isOriginTop : false,
34824     /**
34825      * @cfg {Boolean} isLayoutInstant = no animation?
34826      */   
34827     isLayoutInstant : false, // needed?
34828     /**
34829      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34830      */   
34831     isResizingContainer : true,
34832     /**
34833      * @cfg {Number} columnWidth  width of the columns 
34834      */   
34835     
34836     columnWidth : 0,
34837     
34838     /**
34839      * @cfg {Number} maxCols maximum number of columns
34840      */   
34841     
34842     maxCols: 0,
34843     /**
34844      * @cfg {Number} padHeight padding below box..
34845      */   
34846     
34847     padHeight : 10, 
34848     
34849     /**
34850      * @cfg {Boolean} isAutoInitial defalut true
34851      */   
34852     
34853     isAutoInitial : true, 
34854     
34855     // private?
34856     gutter : 0,
34857     
34858     containerWidth: 0,
34859     initialColumnWidth : 0,
34860     currentSize : null,
34861     
34862     colYs : null, // array.
34863     maxY : 0,
34864     padWidth: 10,
34865     
34866     
34867     tag: 'div',
34868     cls: '',
34869     bricks: null, //CompositeElement
34870     cols : 0, // array?
34871     // element : null, // wrapped now this.el
34872     _isLayoutInited : null, 
34873     
34874     
34875     getAutoCreate : function(){
34876         
34877         var cfg = {
34878             tag: this.tag,
34879             cls: 'blog-masonary-wrapper ' + this.cls,
34880             cn : {
34881                 cls : 'mas-boxes masonary'
34882             }
34883         };
34884         
34885         return cfg;
34886     },
34887     
34888     getChildContainer: function( )
34889     {
34890         if (this.boxesEl) {
34891             return this.boxesEl;
34892         }
34893         
34894         this.boxesEl = this.el.select('.mas-boxes').first();
34895         
34896         return this.boxesEl;
34897     },
34898     
34899     
34900     initEvents : function()
34901     {
34902         var _this = this;
34903         
34904         if(this.isAutoInitial){
34905             Roo.log('hook children rendered');
34906             this.on('childrenrendered', function() {
34907                 Roo.log('children rendered');
34908                 _this.initial();
34909             } ,this);
34910         }
34911         
34912     },
34913     
34914     initial : function()
34915     {
34916         this.reloadItems();
34917
34918         this.currentSize = this.el.getBox(true);
34919
34920         /// was window resize... - let's see if this works..
34921         Roo.EventManager.onWindowResize(this.resize, this); 
34922
34923         if(!this.isAutoInitial){
34924             this.layout();
34925             return;
34926         }
34927         
34928         this.layout.defer(500,this);
34929     },
34930     
34931     reloadItems: function()
34932     {
34933         this.bricks = this.el.select('.masonry-brick', true);
34934         
34935         this.bricks.each(function(b) {
34936             //Roo.log(b.getSize());
34937             if (!b.attr('originalwidth')) {
34938                 b.attr('originalwidth',  b.getSize().width);
34939             }
34940             
34941         });
34942         
34943         Roo.log(this.bricks.elements.length);
34944     },
34945     
34946     resize : function()
34947     {
34948         Roo.log('resize');
34949         var cs = this.el.getBox(true);
34950         
34951         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34952             Roo.log("no change in with or X");
34953             return;
34954         }
34955         this.currentSize = cs;
34956         this.layout();
34957     },
34958     
34959     layout : function()
34960     {
34961          Roo.log('layout');
34962         this._resetLayout();
34963         //this._manageStamps();
34964       
34965         // don't animate first layout
34966         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34967         this.layoutItems( isInstant );
34968       
34969         // flag for initalized
34970         this._isLayoutInited = true;
34971     },
34972     
34973     layoutItems : function( isInstant )
34974     {
34975         //var items = this._getItemsForLayout( this.items );
34976         // original code supports filtering layout items.. we just ignore it..
34977         
34978         this._layoutItems( this.bricks , isInstant );
34979       
34980         this._postLayout();
34981     },
34982     _layoutItems : function ( items , isInstant)
34983     {
34984        //this.fireEvent( 'layout', this, items );
34985     
34986
34987         if ( !items || !items.elements.length ) {
34988           // no items, emit event with empty array
34989             return;
34990         }
34991
34992         var queue = [];
34993         items.each(function(item) {
34994             Roo.log("layout item");
34995             Roo.log(item);
34996             // get x/y object from method
34997             var position = this._getItemLayoutPosition( item );
34998             // enqueue
34999             position.item = item;
35000             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
35001             queue.push( position );
35002         }, this);
35003       
35004         this._processLayoutQueue( queue );
35005     },
35006     /** Sets position of item in DOM
35007     * @param {Element} item
35008     * @param {Number} x - horizontal position
35009     * @param {Number} y - vertical position
35010     * @param {Boolean} isInstant - disables transitions
35011     */
35012     _processLayoutQueue : function( queue )
35013     {
35014         for ( var i=0, len = queue.length; i < len; i++ ) {
35015             var obj = queue[i];
35016             obj.item.position('absolute');
35017             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
35018         }
35019     },
35020       
35021     
35022     /**
35023     * Any logic you want to do after each layout,
35024     * i.e. size the container
35025     */
35026     _postLayout : function()
35027     {
35028         this.resizeContainer();
35029     },
35030     
35031     resizeContainer : function()
35032     {
35033         if ( !this.isResizingContainer ) {
35034             return;
35035         }
35036         var size = this._getContainerSize();
35037         if ( size ) {
35038             this.el.setSize(size.width,size.height);
35039             this.boxesEl.setSize(size.width,size.height);
35040         }
35041     },
35042     
35043     
35044     
35045     _resetLayout : function()
35046     {
35047         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
35048         this.colWidth = this.el.getWidth();
35049         //this.gutter = this.el.getWidth(); 
35050         
35051         this.measureColumns();
35052
35053         // reset column Y
35054         var i = this.cols;
35055         this.colYs = [];
35056         while (i--) {
35057             this.colYs.push( 0 );
35058         }
35059     
35060         this.maxY = 0;
35061     },
35062
35063     measureColumns : function()
35064     {
35065         this.getContainerWidth();
35066       // if columnWidth is 0, default to outerWidth of first item
35067         if ( !this.columnWidth ) {
35068             var firstItem = this.bricks.first();
35069             Roo.log(firstItem);
35070             this.columnWidth  = this.containerWidth;
35071             if (firstItem && firstItem.attr('originalwidth') ) {
35072                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35073             }
35074             // columnWidth fall back to item of first element
35075             Roo.log("set column width?");
35076                         this.initialColumnWidth = this.columnWidth  ;
35077
35078             // if first elem has no width, default to size of container
35079             
35080         }
35081         
35082         
35083         if (this.initialColumnWidth) {
35084             this.columnWidth = this.initialColumnWidth;
35085         }
35086         
35087         
35088             
35089         // column width is fixed at the top - however if container width get's smaller we should
35090         // reduce it...
35091         
35092         // this bit calcs how man columns..
35093             
35094         var columnWidth = this.columnWidth += this.gutter;
35095       
35096         // calculate columns
35097         var containerWidth = this.containerWidth + this.gutter;
35098         
35099         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35100         // fix rounding errors, typically with gutters
35101         var excess = columnWidth - containerWidth % columnWidth;
35102         
35103         
35104         // if overshoot is less than a pixel, round up, otherwise floor it
35105         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35106         cols = Math[ mathMethod ]( cols );
35107         this.cols = Math.max( cols, 1 );
35108         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35109         
35110          // padding positioning..
35111         var totalColWidth = this.cols * this.columnWidth;
35112         var padavail = this.containerWidth - totalColWidth;
35113         // so for 2 columns - we need 3 'pads'
35114         
35115         var padNeeded = (1+this.cols) * this.padWidth;
35116         
35117         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35118         
35119         this.columnWidth += padExtra
35120         //this.padWidth = Math.floor(padavail /  ( this.cols));
35121         
35122         // adjust colum width so that padding is fixed??
35123         
35124         // we have 3 columns ... total = width * 3
35125         // we have X left over... that should be used by 
35126         
35127         //if (this.expandC) {
35128             
35129         //}
35130         
35131         
35132         
35133     },
35134     
35135     getContainerWidth : function()
35136     {
35137        /* // container is parent if fit width
35138         var container = this.isFitWidth ? this.element.parentNode : this.element;
35139         // check that this.size and size are there
35140         // IE8 triggers resize on body size change, so they might not be
35141         
35142         var size = getSize( container );  //FIXME
35143         this.containerWidth = size && size.innerWidth; //FIXME
35144         */
35145          
35146         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35147         
35148     },
35149     
35150     _getItemLayoutPosition : function( item )  // what is item?
35151     {
35152         // we resize the item to our columnWidth..
35153       
35154         item.setWidth(this.columnWidth);
35155         item.autoBoxAdjust  = false;
35156         
35157         var sz = item.getSize();
35158  
35159         // how many columns does this brick span
35160         var remainder = this.containerWidth % this.columnWidth;
35161         
35162         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35163         // round if off by 1 pixel, otherwise use ceil
35164         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35165         colSpan = Math.min( colSpan, this.cols );
35166         
35167         // normally this should be '1' as we dont' currently allow multi width columns..
35168         
35169         var colGroup = this._getColGroup( colSpan );
35170         // get the minimum Y value from the columns
35171         var minimumY = Math.min.apply( Math, colGroup );
35172         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35173         
35174         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35175          
35176         // position the brick
35177         var position = {
35178             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35179             y: this.currentSize.y + minimumY + this.padHeight
35180         };
35181         
35182         Roo.log(position);
35183         // apply setHeight to necessary columns
35184         var setHeight = minimumY + sz.height + this.padHeight;
35185         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35186         
35187         var setSpan = this.cols + 1 - colGroup.length;
35188         for ( var i = 0; i < setSpan; i++ ) {
35189           this.colYs[ shortColIndex + i ] = setHeight ;
35190         }
35191       
35192         return position;
35193     },
35194     
35195     /**
35196      * @param {Number} colSpan - number of columns the element spans
35197      * @returns {Array} colGroup
35198      */
35199     _getColGroup : function( colSpan )
35200     {
35201         if ( colSpan < 2 ) {
35202           // if brick spans only one column, use all the column Ys
35203           return this.colYs;
35204         }
35205       
35206         var colGroup = [];
35207         // how many different places could this brick fit horizontally
35208         var groupCount = this.cols + 1 - colSpan;
35209         // for each group potential horizontal position
35210         for ( var i = 0; i < groupCount; i++ ) {
35211           // make an array of colY values for that one group
35212           var groupColYs = this.colYs.slice( i, i + colSpan );
35213           // and get the max value of the array
35214           colGroup[i] = Math.max.apply( Math, groupColYs );
35215         }
35216         return colGroup;
35217     },
35218     /*
35219     _manageStamp : function( stamp )
35220     {
35221         var stampSize =  stamp.getSize();
35222         var offset = stamp.getBox();
35223         // get the columns that this stamp affects
35224         var firstX = this.isOriginLeft ? offset.x : offset.right;
35225         var lastX = firstX + stampSize.width;
35226         var firstCol = Math.floor( firstX / this.columnWidth );
35227         firstCol = Math.max( 0, firstCol );
35228         
35229         var lastCol = Math.floor( lastX / this.columnWidth );
35230         // lastCol should not go over if multiple of columnWidth #425
35231         lastCol -= lastX % this.columnWidth ? 0 : 1;
35232         lastCol = Math.min( this.cols - 1, lastCol );
35233         
35234         // set colYs to bottom of the stamp
35235         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35236             stampSize.height;
35237             
35238         for ( var i = firstCol; i <= lastCol; i++ ) {
35239           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35240         }
35241     },
35242     */
35243     
35244     _getContainerSize : function()
35245     {
35246         this.maxY = Math.max.apply( Math, this.colYs );
35247         var size = {
35248             height: this.maxY
35249         };
35250       
35251         if ( this.isFitWidth ) {
35252             size.width = this._getContainerFitWidth();
35253         }
35254       
35255         return size;
35256     },
35257     
35258     _getContainerFitWidth : function()
35259     {
35260         var unusedCols = 0;
35261         // count unused columns
35262         var i = this.cols;
35263         while ( --i ) {
35264           if ( this.colYs[i] !== 0 ) {
35265             break;
35266           }
35267           unusedCols++;
35268         }
35269         // fit container to columns that have been used
35270         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35271     },
35272     
35273     needsResizeLayout : function()
35274     {
35275         var previousWidth = this.containerWidth;
35276         this.getContainerWidth();
35277         return previousWidth !== this.containerWidth;
35278     }
35279  
35280 });
35281
35282  
35283
35284  /*
35285  * - LGPL
35286  *
35287  * element
35288  * 
35289  */
35290
35291 /**
35292  * @class Roo.bootstrap.MasonryBrick
35293  * @extends Roo.bootstrap.Component
35294  * Bootstrap MasonryBrick class
35295  * 
35296  * @constructor
35297  * Create a new MasonryBrick
35298  * @param {Object} config The config object
35299  */
35300
35301 Roo.bootstrap.MasonryBrick = function(config){
35302     
35303     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35304     
35305     Roo.bootstrap.MasonryBrick.register(this);
35306     
35307     this.addEvents({
35308         // raw events
35309         /**
35310          * @event click
35311          * When a MasonryBrick is clcik
35312          * @param {Roo.bootstrap.MasonryBrick} this
35313          * @param {Roo.EventObject} e
35314          */
35315         "click" : true
35316     });
35317 };
35318
35319 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35320     
35321     /**
35322      * @cfg {String} title
35323      */   
35324     title : '',
35325     /**
35326      * @cfg {String} html
35327      */   
35328     html : '',
35329     /**
35330      * @cfg {String} bgimage
35331      */   
35332     bgimage : '',
35333     /**
35334      * @cfg {String} videourl
35335      */   
35336     videourl : '',
35337     /**
35338      * @cfg {String} cls
35339      */   
35340     cls : '',
35341     /**
35342      * @cfg {String} href
35343      */   
35344     href : '',
35345     /**
35346      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35347      */   
35348     size : 'xs',
35349     
35350     /**
35351      * @cfg {String} placetitle (center|bottom)
35352      */   
35353     placetitle : '',
35354     
35355     /**
35356      * @cfg {Boolean} isFitContainer defalut true
35357      */   
35358     isFitContainer : true, 
35359     
35360     /**
35361      * @cfg {Boolean} preventDefault defalut false
35362      */   
35363     preventDefault : false, 
35364     
35365     /**
35366      * @cfg {Boolean} inverse defalut false
35367      */   
35368     maskInverse : false, 
35369     
35370     getAutoCreate : function()
35371     {
35372         if(!this.isFitContainer){
35373             return this.getSplitAutoCreate();
35374         }
35375         
35376         var cls = 'masonry-brick masonry-brick-full';
35377         
35378         if(this.href.length){
35379             cls += ' masonry-brick-link';
35380         }
35381         
35382         if(this.bgimage.length){
35383             cls += ' masonry-brick-image';
35384         }
35385         
35386         if(this.maskInverse){
35387             cls += ' mask-inverse';
35388         }
35389         
35390         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35391             cls += ' enable-mask';
35392         }
35393         
35394         if(this.size){
35395             cls += ' masonry-' + this.size + '-brick';
35396         }
35397         
35398         if(this.placetitle.length){
35399             
35400             switch (this.placetitle) {
35401                 case 'center' :
35402                     cls += ' masonry-center-title';
35403                     break;
35404                 case 'bottom' :
35405                     cls += ' masonry-bottom-title';
35406                     break;
35407                 default:
35408                     break;
35409             }
35410             
35411         } else {
35412             if(!this.html.length && !this.bgimage.length){
35413                 cls += ' masonry-center-title';
35414             }
35415
35416             if(!this.html.length && this.bgimage.length){
35417                 cls += ' masonry-bottom-title';
35418             }
35419         }
35420         
35421         if(this.cls){
35422             cls += ' ' + this.cls;
35423         }
35424         
35425         var cfg = {
35426             tag: (this.href.length) ? 'a' : 'div',
35427             cls: cls,
35428             cn: [
35429                 {
35430                     tag: 'div',
35431                     cls: 'masonry-brick-mask'
35432                 },
35433                 {
35434                     tag: 'div',
35435                     cls: 'masonry-brick-paragraph',
35436                     cn: []
35437                 }
35438             ]
35439         };
35440         
35441         if(this.href.length){
35442             cfg.href = this.href;
35443         }
35444         
35445         var cn = cfg.cn[1].cn;
35446         
35447         if(this.title.length){
35448             cn.push({
35449                 tag: 'h4',
35450                 cls: 'masonry-brick-title',
35451                 html: this.title
35452             });
35453         }
35454         
35455         if(this.html.length){
35456             cn.push({
35457                 tag: 'p',
35458                 cls: 'masonry-brick-text',
35459                 html: this.html
35460             });
35461         }
35462         
35463         if (!this.title.length && !this.html.length) {
35464             cfg.cn[1].cls += ' hide';
35465         }
35466         
35467         if(this.bgimage.length){
35468             cfg.cn.push({
35469                 tag: 'img',
35470                 cls: 'masonry-brick-image-view',
35471                 src: this.bgimage
35472             });
35473         }
35474         
35475         if(this.videourl.length){
35476             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35477             // youtube support only?
35478             cfg.cn.push({
35479                 tag: 'iframe',
35480                 cls: 'masonry-brick-image-view',
35481                 src: vurl,
35482                 frameborder : 0,
35483                 allowfullscreen : true
35484             });
35485         }
35486         
35487         return cfg;
35488         
35489     },
35490     
35491     getSplitAutoCreate : function()
35492     {
35493         var cls = 'masonry-brick masonry-brick-split';
35494         
35495         if(this.href.length){
35496             cls += ' masonry-brick-link';
35497         }
35498         
35499         if(this.bgimage.length){
35500             cls += ' masonry-brick-image';
35501         }
35502         
35503         if(this.size){
35504             cls += ' masonry-' + this.size + '-brick';
35505         }
35506         
35507         switch (this.placetitle) {
35508             case 'center' :
35509                 cls += ' masonry-center-title';
35510                 break;
35511             case 'bottom' :
35512                 cls += ' masonry-bottom-title';
35513                 break;
35514             default:
35515                 if(!this.bgimage.length){
35516                     cls += ' masonry-center-title';
35517                 }
35518
35519                 if(this.bgimage.length){
35520                     cls += ' masonry-bottom-title';
35521                 }
35522                 break;
35523         }
35524         
35525         if(this.cls){
35526             cls += ' ' + this.cls;
35527         }
35528         
35529         var cfg = {
35530             tag: (this.href.length) ? 'a' : 'div',
35531             cls: cls,
35532             cn: [
35533                 {
35534                     tag: 'div',
35535                     cls: 'masonry-brick-split-head',
35536                     cn: [
35537                         {
35538                             tag: 'div',
35539                             cls: 'masonry-brick-paragraph',
35540                             cn: []
35541                         }
35542                     ]
35543                 },
35544                 {
35545                     tag: 'div',
35546                     cls: 'masonry-brick-split-body',
35547                     cn: []
35548                 }
35549             ]
35550         };
35551         
35552         if(this.href.length){
35553             cfg.href = this.href;
35554         }
35555         
35556         if(this.title.length){
35557             cfg.cn[0].cn[0].cn.push({
35558                 tag: 'h4',
35559                 cls: 'masonry-brick-title',
35560                 html: this.title
35561             });
35562         }
35563         
35564         if(this.html.length){
35565             cfg.cn[1].cn.push({
35566                 tag: 'p',
35567                 cls: 'masonry-brick-text',
35568                 html: this.html
35569             });
35570         }
35571
35572         if(this.bgimage.length){
35573             cfg.cn[0].cn.push({
35574                 tag: 'img',
35575                 cls: 'masonry-brick-image-view',
35576                 src: this.bgimage
35577             });
35578         }
35579         
35580         if(this.videourl.length){
35581             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35582             // youtube support only?
35583             cfg.cn[0].cn.cn.push({
35584                 tag: 'iframe',
35585                 cls: 'masonry-brick-image-view',
35586                 src: vurl,
35587                 frameborder : 0,
35588                 allowfullscreen : true
35589             });
35590         }
35591         
35592         return cfg;
35593     },
35594     
35595     initEvents: function() 
35596     {
35597         switch (this.size) {
35598             case 'xs' :
35599                 this.x = 1;
35600                 this.y = 1;
35601                 break;
35602             case 'sm' :
35603                 this.x = 2;
35604                 this.y = 2;
35605                 break;
35606             case 'md' :
35607             case 'md-left' :
35608             case 'md-right' :
35609                 this.x = 3;
35610                 this.y = 3;
35611                 break;
35612             case 'tall' :
35613                 this.x = 2;
35614                 this.y = 3;
35615                 break;
35616             case 'wide' :
35617                 this.x = 3;
35618                 this.y = 2;
35619                 break;
35620             case 'wide-thin' :
35621                 this.x = 3;
35622                 this.y = 1;
35623                 break;
35624                         
35625             default :
35626                 break;
35627         }
35628         
35629         if(Roo.isTouch){
35630             this.el.on('touchstart', this.onTouchStart, this);
35631             this.el.on('touchmove', this.onTouchMove, this);
35632             this.el.on('touchend', this.onTouchEnd, this);
35633             this.el.on('contextmenu', this.onContextMenu, this);
35634         } else {
35635             this.el.on('mouseenter'  ,this.enter, this);
35636             this.el.on('mouseleave', this.leave, this);
35637             this.el.on('click', this.onClick, this);
35638         }
35639         
35640         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35641             this.parent().bricks.push(this);   
35642         }
35643         
35644     },
35645     
35646     onClick: function(e, el)
35647     {
35648         var time = this.endTimer - this.startTimer;
35649         // Roo.log(e.preventDefault());
35650         if(Roo.isTouch){
35651             if(time > 1000){
35652                 e.preventDefault();
35653                 return;
35654             }
35655         }
35656         
35657         if(!this.preventDefault){
35658             return;
35659         }
35660         
35661         e.preventDefault();
35662         
35663         if (this.activeClass != '') {
35664             this.selectBrick();
35665         }
35666         
35667         this.fireEvent('click', this, e);
35668     },
35669     
35670     enter: function(e, el)
35671     {
35672         e.preventDefault();
35673         
35674         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35675             return;
35676         }
35677         
35678         if(this.bgimage.length && this.html.length){
35679             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35680         }
35681     },
35682     
35683     leave: function(e, el)
35684     {
35685         e.preventDefault();
35686         
35687         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35688             return;
35689         }
35690         
35691         if(this.bgimage.length && this.html.length){
35692             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35693         }
35694     },
35695     
35696     onTouchStart: function(e, el)
35697     {
35698 //        e.preventDefault();
35699         
35700         this.touchmoved = false;
35701         
35702         if(!this.isFitContainer){
35703             return;
35704         }
35705         
35706         if(!this.bgimage.length || !this.html.length){
35707             return;
35708         }
35709         
35710         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35711         
35712         this.timer = new Date().getTime();
35713         
35714     },
35715     
35716     onTouchMove: function(e, el)
35717     {
35718         this.touchmoved = true;
35719     },
35720     
35721     onContextMenu : function(e,el)
35722     {
35723         e.preventDefault();
35724         e.stopPropagation();
35725         return false;
35726     },
35727     
35728     onTouchEnd: function(e, el)
35729     {
35730 //        e.preventDefault();
35731         
35732         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35733         
35734             this.leave(e,el);
35735             
35736             return;
35737         }
35738         
35739         if(!this.bgimage.length || !this.html.length){
35740             
35741             if(this.href.length){
35742                 window.location.href = this.href;
35743             }
35744             
35745             return;
35746         }
35747         
35748         if(!this.isFitContainer){
35749             return;
35750         }
35751         
35752         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35753         
35754         window.location.href = this.href;
35755     },
35756     
35757     //selection on single brick only
35758     selectBrick : function() {
35759         
35760         if (!this.parentId) {
35761             return;
35762         }
35763         
35764         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35765         var index = m.selectedBrick.indexOf(this.id);
35766         
35767         if ( index > -1) {
35768             m.selectedBrick.splice(index,1);
35769             this.el.removeClass(this.activeClass);
35770             return;
35771         }
35772         
35773         for(var i = 0; i < m.selectedBrick.length; i++) {
35774             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35775             b.el.removeClass(b.activeClass);
35776         }
35777         
35778         m.selectedBrick = [];
35779         
35780         m.selectedBrick.push(this.id);
35781         this.el.addClass(this.activeClass);
35782         return;
35783     },
35784     
35785     isSelected : function(){
35786         return this.el.hasClass(this.activeClass);
35787         
35788     }
35789 });
35790
35791 Roo.apply(Roo.bootstrap.MasonryBrick, {
35792     
35793     //groups: {},
35794     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35795      /**
35796     * register a Masonry Brick
35797     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35798     */
35799     
35800     register : function(brick)
35801     {
35802         //this.groups[brick.id] = brick;
35803         this.groups.add(brick.id, brick);
35804     },
35805     /**
35806     * fetch a  masonry brick based on the masonry brick ID
35807     * @param {string} the masonry brick to add
35808     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35809     */
35810     
35811     get: function(brick_id) 
35812     {
35813         // if (typeof(this.groups[brick_id]) == 'undefined') {
35814         //     return false;
35815         // }
35816         // return this.groups[brick_id] ;
35817         
35818         if(this.groups.key(brick_id)) {
35819             return this.groups.key(brick_id);
35820         }
35821         
35822         return false;
35823     }
35824     
35825     
35826     
35827 });
35828
35829  /*
35830  * - LGPL
35831  *
35832  * element
35833  * 
35834  */
35835
35836 /**
35837  * @class Roo.bootstrap.Brick
35838  * @extends Roo.bootstrap.Component
35839  * Bootstrap Brick class
35840  * 
35841  * @constructor
35842  * Create a new Brick
35843  * @param {Object} config The config object
35844  */
35845
35846 Roo.bootstrap.Brick = function(config){
35847     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35848     
35849     this.addEvents({
35850         // raw events
35851         /**
35852          * @event click
35853          * When a Brick is click
35854          * @param {Roo.bootstrap.Brick} this
35855          * @param {Roo.EventObject} e
35856          */
35857         "click" : true
35858     });
35859 };
35860
35861 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35862     
35863     /**
35864      * @cfg {String} title
35865      */   
35866     title : '',
35867     /**
35868      * @cfg {String} html
35869      */   
35870     html : '',
35871     /**
35872      * @cfg {String} bgimage
35873      */   
35874     bgimage : '',
35875     /**
35876      * @cfg {String} cls
35877      */   
35878     cls : '',
35879     /**
35880      * @cfg {String} href
35881      */   
35882     href : '',
35883     /**
35884      * @cfg {String} video
35885      */   
35886     video : '',
35887     /**
35888      * @cfg {Boolean} square
35889      */   
35890     square : true,
35891     
35892     getAutoCreate : function()
35893     {
35894         var cls = 'roo-brick';
35895         
35896         if(this.href.length){
35897             cls += ' roo-brick-link';
35898         }
35899         
35900         if(this.bgimage.length){
35901             cls += ' roo-brick-image';
35902         }
35903         
35904         if(!this.html.length && !this.bgimage.length){
35905             cls += ' roo-brick-center-title';
35906         }
35907         
35908         if(!this.html.length && this.bgimage.length){
35909             cls += ' roo-brick-bottom-title';
35910         }
35911         
35912         if(this.cls){
35913             cls += ' ' + this.cls;
35914         }
35915         
35916         var cfg = {
35917             tag: (this.href.length) ? 'a' : 'div',
35918             cls: cls,
35919             cn: [
35920                 {
35921                     tag: 'div',
35922                     cls: 'roo-brick-paragraph',
35923                     cn: []
35924                 }
35925             ]
35926         };
35927         
35928         if(this.href.length){
35929             cfg.href = this.href;
35930         }
35931         
35932         var cn = cfg.cn[0].cn;
35933         
35934         if(this.title.length){
35935             cn.push({
35936                 tag: 'h4',
35937                 cls: 'roo-brick-title',
35938                 html: this.title
35939             });
35940         }
35941         
35942         if(this.html.length){
35943             cn.push({
35944                 tag: 'p',
35945                 cls: 'roo-brick-text',
35946                 html: this.html
35947             });
35948         } else {
35949             cn.cls += ' hide';
35950         }
35951         
35952         if(this.bgimage.length){
35953             cfg.cn.push({
35954                 tag: 'img',
35955                 cls: 'roo-brick-image-view',
35956                 src: this.bgimage
35957             });
35958         }
35959         
35960         return cfg;
35961     },
35962     
35963     initEvents: function() 
35964     {
35965         if(this.title.length || this.html.length){
35966             this.el.on('mouseenter'  ,this.enter, this);
35967             this.el.on('mouseleave', this.leave, this);
35968         }
35969         
35970         Roo.EventManager.onWindowResize(this.resize, this); 
35971         
35972         if(this.bgimage.length){
35973             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35974             this.imageEl.on('load', this.onImageLoad, this);
35975             return;
35976         }
35977         
35978         this.resize();
35979     },
35980     
35981     onImageLoad : function()
35982     {
35983         this.resize();
35984     },
35985     
35986     resize : function()
35987     {
35988         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35989         
35990         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35991         
35992         if(this.bgimage.length){
35993             var image = this.el.select('.roo-brick-image-view', true).first();
35994             
35995             image.setWidth(paragraph.getWidth());
35996             
35997             if(this.square){
35998                 image.setHeight(paragraph.getWidth());
35999             }
36000             
36001             this.el.setHeight(image.getHeight());
36002             paragraph.setHeight(image.getHeight());
36003             
36004         }
36005         
36006     },
36007     
36008     enter: function(e, el)
36009     {
36010         e.preventDefault();
36011         
36012         if(this.bgimage.length){
36013             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
36014             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
36015         }
36016     },
36017     
36018     leave: function(e, el)
36019     {
36020         e.preventDefault();
36021         
36022         if(this.bgimage.length){
36023             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
36024             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
36025         }
36026     }
36027     
36028 });
36029
36030  
36031
36032  /*
36033  * - LGPL
36034  *
36035  * Number field 
36036  */
36037
36038 /**
36039  * @class Roo.bootstrap.NumberField
36040  * @extends Roo.bootstrap.Input
36041  * Bootstrap NumberField class
36042  * 
36043  * 
36044  * 
36045  * 
36046  * @constructor
36047  * Create a new NumberField
36048  * @param {Object} config The config object
36049  */
36050
36051 Roo.bootstrap.NumberField = function(config){
36052     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36053 };
36054
36055 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36056     
36057     /**
36058      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36059      */
36060     allowDecimals : true,
36061     /**
36062      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36063      */
36064     decimalSeparator : ".",
36065     /**
36066      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36067      */
36068     decimalPrecision : 2,
36069     /**
36070      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36071      */
36072     allowNegative : true,
36073     
36074     /**
36075      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36076      */
36077     allowZero: true,
36078     /**
36079      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36080      */
36081     minValue : Number.NEGATIVE_INFINITY,
36082     /**
36083      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36084      */
36085     maxValue : Number.MAX_VALUE,
36086     /**
36087      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36088      */
36089     minText : "The minimum value for this field is {0}",
36090     /**
36091      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36092      */
36093     maxText : "The maximum value for this field is {0}",
36094     /**
36095      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36096      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36097      */
36098     nanText : "{0} is not a valid number",
36099     /**
36100      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36101      */
36102     thousandsDelimiter : false,
36103     /**
36104      * @cfg {String} valueAlign alignment of value
36105      */
36106     valueAlign : "left",
36107
36108     getAutoCreate : function()
36109     {
36110         var hiddenInput = {
36111             tag: 'input',
36112             type: 'hidden',
36113             id: Roo.id(),
36114             cls: 'hidden-number-input'
36115         };
36116         
36117         if (this.name) {
36118             hiddenInput.name = this.name;
36119         }
36120         
36121         this.name = '';
36122         
36123         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36124         
36125         this.name = hiddenInput.name;
36126         
36127         if(cfg.cn.length > 0) {
36128             cfg.cn.push(hiddenInput);
36129         }
36130         
36131         return cfg;
36132     },
36133
36134     // private
36135     initEvents : function()
36136     {   
36137         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36138         
36139         var allowed = "0123456789";
36140         
36141         if(this.allowDecimals){
36142             allowed += this.decimalSeparator;
36143         }
36144         
36145         if(this.allowNegative){
36146             allowed += "-";
36147         }
36148         
36149         if(this.thousandsDelimiter) {
36150             allowed += ",";
36151         }
36152         
36153         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36154         
36155         var keyPress = function(e){
36156             
36157             var k = e.getKey();
36158             
36159             var c = e.getCharCode();
36160             
36161             if(
36162                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36163                     allowed.indexOf(String.fromCharCode(c)) === -1
36164             ){
36165                 e.stopEvent();
36166                 return;
36167             }
36168             
36169             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36170                 return;
36171             }
36172             
36173             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36174                 e.stopEvent();
36175             }
36176         };
36177         
36178         this.el.on("keypress", keyPress, this);
36179     },
36180     
36181     validateValue : function(value)
36182     {
36183         
36184         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36185             return false;
36186         }
36187         
36188         var num = this.parseValue(value);
36189         
36190         if(isNaN(num)){
36191             this.markInvalid(String.format(this.nanText, value));
36192             return false;
36193         }
36194         
36195         if(num < this.minValue){
36196             this.markInvalid(String.format(this.minText, this.minValue));
36197             return false;
36198         }
36199         
36200         if(num > this.maxValue){
36201             this.markInvalid(String.format(this.maxText, this.maxValue));
36202             return false;
36203         }
36204         
36205         return true;
36206     },
36207
36208     getValue : function()
36209     {
36210         var v = this.hiddenEl().getValue();
36211         
36212         return this.fixPrecision(this.parseValue(v));
36213     },
36214
36215     parseValue : function(value)
36216     {
36217         if(this.thousandsDelimiter) {
36218             value += "";
36219             r = new RegExp(",", "g");
36220             value = value.replace(r, "");
36221         }
36222         
36223         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36224         return isNaN(value) ? '' : value;
36225     },
36226
36227     fixPrecision : function(value)
36228     {
36229         if(this.thousandsDelimiter) {
36230             value += "";
36231             r = new RegExp(",", "g");
36232             value = value.replace(r, "");
36233         }
36234         
36235         var nan = isNaN(value);
36236         
36237         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36238             return nan ? '' : value;
36239         }
36240         return parseFloat(value).toFixed(this.decimalPrecision);
36241     },
36242
36243     setValue : function(v)
36244     {
36245         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36246         
36247         this.value = v;
36248         
36249         if(this.rendered){
36250             
36251             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36252             
36253             this.inputEl().dom.value = (v == '') ? '' :
36254                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36255             
36256             if(!this.allowZero && v === '0') {
36257                 this.hiddenEl().dom.value = '';
36258                 this.inputEl().dom.value = '';
36259             }
36260             
36261             this.validate();
36262         }
36263     },
36264
36265     decimalPrecisionFcn : function(v)
36266     {
36267         return Math.floor(v);
36268     },
36269
36270     beforeBlur : function()
36271     {
36272         var v = this.parseValue(this.getRawValue());
36273         
36274         if(v || v === 0 || v === ''){
36275             this.setValue(v);
36276         }
36277     },
36278     
36279     hiddenEl : function()
36280     {
36281         return this.el.select('input.hidden-number-input',true).first();
36282     }
36283     
36284 });
36285
36286  
36287
36288 /*
36289 * Licence: LGPL
36290 */
36291
36292 /**
36293  * @class Roo.bootstrap.DocumentSlider
36294  * @extends Roo.bootstrap.Component
36295  * Bootstrap DocumentSlider class
36296  * 
36297  * @constructor
36298  * Create a new DocumentViewer
36299  * @param {Object} config The config object
36300  */
36301
36302 Roo.bootstrap.DocumentSlider = function(config){
36303     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36304     
36305     this.files = [];
36306     
36307     this.addEvents({
36308         /**
36309          * @event initial
36310          * Fire after initEvent
36311          * @param {Roo.bootstrap.DocumentSlider} this
36312          */
36313         "initial" : true,
36314         /**
36315          * @event update
36316          * Fire after update
36317          * @param {Roo.bootstrap.DocumentSlider} this
36318          */
36319         "update" : true,
36320         /**
36321          * @event click
36322          * Fire after click
36323          * @param {Roo.bootstrap.DocumentSlider} this
36324          */
36325         "click" : true
36326     });
36327 };
36328
36329 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36330     
36331     files : false,
36332     
36333     indicator : 0,
36334     
36335     getAutoCreate : function()
36336     {
36337         var cfg = {
36338             tag : 'div',
36339             cls : 'roo-document-slider',
36340             cn : [
36341                 {
36342                     tag : 'div',
36343                     cls : 'roo-document-slider-header',
36344                     cn : [
36345                         {
36346                             tag : 'div',
36347                             cls : 'roo-document-slider-header-title'
36348                         }
36349                     ]
36350                 },
36351                 {
36352                     tag : 'div',
36353                     cls : 'roo-document-slider-body',
36354                     cn : [
36355                         {
36356                             tag : 'div',
36357                             cls : 'roo-document-slider-prev',
36358                             cn : [
36359                                 {
36360                                     tag : 'i',
36361                                     cls : 'fa fa-chevron-left'
36362                                 }
36363                             ]
36364                         },
36365                         {
36366                             tag : 'div',
36367                             cls : 'roo-document-slider-thumb',
36368                             cn : [
36369                                 {
36370                                     tag : 'img',
36371                                     cls : 'roo-document-slider-image'
36372                                 }
36373                             ]
36374                         },
36375                         {
36376                             tag : 'div',
36377                             cls : 'roo-document-slider-next',
36378                             cn : [
36379                                 {
36380                                     tag : 'i',
36381                                     cls : 'fa fa-chevron-right'
36382                                 }
36383                             ]
36384                         }
36385                     ]
36386                 }
36387             ]
36388         };
36389         
36390         return cfg;
36391     },
36392     
36393     initEvents : function()
36394     {
36395         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36396         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36397         
36398         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36399         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36400         
36401         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36402         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36403         
36404         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36405         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36406         
36407         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36408         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36409         
36410         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36411         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36412         
36413         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36414         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36415         
36416         this.thumbEl.on('click', this.onClick, this);
36417         
36418         this.prevIndicator.on('click', this.prev, this);
36419         
36420         this.nextIndicator.on('click', this.next, this);
36421         
36422     },
36423     
36424     initial : function()
36425     {
36426         if(this.files.length){
36427             this.indicator = 1;
36428             this.update()
36429         }
36430         
36431         this.fireEvent('initial', this);
36432     },
36433     
36434     update : function()
36435     {
36436         this.imageEl.attr('src', this.files[this.indicator - 1]);
36437         
36438         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36439         
36440         this.prevIndicator.show();
36441         
36442         if(this.indicator == 1){
36443             this.prevIndicator.hide();
36444         }
36445         
36446         this.nextIndicator.show();
36447         
36448         if(this.indicator == this.files.length){
36449             this.nextIndicator.hide();
36450         }
36451         
36452         this.thumbEl.scrollTo('top');
36453         
36454         this.fireEvent('update', this);
36455     },
36456     
36457     onClick : function(e)
36458     {
36459         e.preventDefault();
36460         
36461         this.fireEvent('click', this);
36462     },
36463     
36464     prev : function(e)
36465     {
36466         e.preventDefault();
36467         
36468         this.indicator = Math.max(1, this.indicator - 1);
36469         
36470         this.update();
36471     },
36472     
36473     next : function(e)
36474     {
36475         e.preventDefault();
36476         
36477         this.indicator = Math.min(this.files.length, this.indicator + 1);
36478         
36479         this.update();
36480     }
36481 });
36482 /*
36483  * - LGPL
36484  *
36485  * RadioSet
36486  *
36487  *
36488  */
36489
36490 /**
36491  * @class Roo.bootstrap.RadioSet
36492  * @extends Roo.bootstrap.Input
36493  * Bootstrap RadioSet class
36494  * @cfg {String} indicatorpos (left|right) default left
36495  * @cfg {Boolean} inline (true|false) inline the element (default true)
36496  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36497  * @constructor
36498  * Create a new RadioSet
36499  * @param {Object} config The config object
36500  */
36501
36502 Roo.bootstrap.RadioSet = function(config){
36503     
36504     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36505     
36506     this.radioes = [];
36507     
36508     Roo.bootstrap.RadioSet.register(this);
36509     
36510     this.addEvents({
36511         /**
36512         * @event check
36513         * Fires when the element is checked or unchecked.
36514         * @param {Roo.bootstrap.RadioSet} this This radio
36515         * @param {Roo.bootstrap.Radio} item The checked item
36516         */
36517        check : true,
36518        /**
36519         * @event click
36520         * Fires when the element is click.
36521         * @param {Roo.bootstrap.RadioSet} this This radio set
36522         * @param {Roo.bootstrap.Radio} item The checked item
36523         * @param {Roo.EventObject} e The event object
36524         */
36525        click : true
36526     });
36527     
36528 };
36529
36530 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36531
36532     radioes : false,
36533     
36534     inline : true,
36535     
36536     weight : '',
36537     
36538     indicatorpos : 'left',
36539     
36540     getAutoCreate : function()
36541     {
36542         var label = {
36543             tag : 'label',
36544             cls : 'roo-radio-set-label',
36545             cn : [
36546                 {
36547                     tag : 'span',
36548                     html : this.fieldLabel
36549                 }
36550             ]
36551         };
36552         if (Roo.bootstrap.version == 3) {
36553             
36554             
36555             if(this.indicatorpos == 'left'){
36556                 label.cn.unshift({
36557                     tag : 'i',
36558                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36559                     tooltip : 'This field is required'
36560                 });
36561             } else {
36562                 label.cn.push({
36563                     tag : 'i',
36564                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36565                     tooltip : 'This field is required'
36566                 });
36567             }
36568         }
36569         var items = {
36570             tag : 'div',
36571             cls : 'roo-radio-set-items'
36572         };
36573         
36574         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36575         
36576         if (align === 'left' && this.fieldLabel.length) {
36577             
36578             items = {
36579                 cls : "roo-radio-set-right", 
36580                 cn: [
36581                     items
36582                 ]
36583             };
36584             
36585             if(this.labelWidth > 12){
36586                 label.style = "width: " + this.labelWidth + 'px';
36587             }
36588             
36589             if(this.labelWidth < 13 && this.labelmd == 0){
36590                 this.labelmd = this.labelWidth;
36591             }
36592             
36593             if(this.labellg > 0){
36594                 label.cls += ' col-lg-' + this.labellg;
36595                 items.cls += ' col-lg-' + (12 - this.labellg);
36596             }
36597             
36598             if(this.labelmd > 0){
36599                 label.cls += ' col-md-' + this.labelmd;
36600                 items.cls += ' col-md-' + (12 - this.labelmd);
36601             }
36602             
36603             if(this.labelsm > 0){
36604                 label.cls += ' col-sm-' + this.labelsm;
36605                 items.cls += ' col-sm-' + (12 - this.labelsm);
36606             }
36607             
36608             if(this.labelxs > 0){
36609                 label.cls += ' col-xs-' + this.labelxs;
36610                 items.cls += ' col-xs-' + (12 - this.labelxs);
36611             }
36612         }
36613         
36614         var cfg = {
36615             tag : 'div',
36616             cls : 'roo-radio-set',
36617             cn : [
36618                 {
36619                     tag : 'input',
36620                     cls : 'roo-radio-set-input',
36621                     type : 'hidden',
36622                     name : this.name,
36623                     value : this.value ? this.value :  ''
36624                 },
36625                 label,
36626                 items
36627             ]
36628         };
36629         
36630         if(this.weight.length){
36631             cfg.cls += ' roo-radio-' + this.weight;
36632         }
36633         
36634         if(this.inline) {
36635             cfg.cls += ' roo-radio-set-inline';
36636         }
36637         
36638         var settings=this;
36639         ['xs','sm','md','lg'].map(function(size){
36640             if (settings[size]) {
36641                 cfg.cls += ' col-' + size + '-' + settings[size];
36642             }
36643         });
36644         
36645         return cfg;
36646         
36647     },
36648
36649     initEvents : function()
36650     {
36651         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36652         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36653         
36654         if(!this.fieldLabel.length){
36655             this.labelEl.hide();
36656         }
36657         
36658         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36659         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36660         
36661         this.indicator = this.indicatorEl();
36662         
36663         if(this.indicator){
36664             this.indicator.addClass('invisible');
36665         }
36666         
36667         this.originalValue = this.getValue();
36668         
36669     },
36670     
36671     inputEl: function ()
36672     {
36673         return this.el.select('.roo-radio-set-input', true).first();
36674     },
36675     
36676     getChildContainer : function()
36677     {
36678         return this.itemsEl;
36679     },
36680     
36681     register : function(item)
36682     {
36683         this.radioes.push(item);
36684         
36685     },
36686     
36687     validate : function()
36688     {   
36689         if(this.getVisibilityEl().hasClass('hidden')){
36690             return true;
36691         }
36692         
36693         var valid = false;
36694         
36695         Roo.each(this.radioes, function(i){
36696             if(!i.checked){
36697                 return;
36698             }
36699             
36700             valid = true;
36701             return false;
36702         });
36703         
36704         if(this.allowBlank) {
36705             return true;
36706         }
36707         
36708         if(this.disabled || valid){
36709             this.markValid();
36710             return true;
36711         }
36712         
36713         this.markInvalid();
36714         return false;
36715         
36716     },
36717     
36718     markValid : function()
36719     {
36720         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36721             this.indicatorEl().removeClass('visible');
36722             this.indicatorEl().addClass('invisible');
36723         }
36724         
36725         
36726         if (Roo.bootstrap.version == 3) {
36727             this.el.removeClass([this.invalidClass, this.validClass]);
36728             this.el.addClass(this.validClass);
36729         } else {
36730             this.el.removeClass(['is-invalid','is-valid']);
36731             this.el.addClass(['is-valid']);
36732         }
36733         this.fireEvent('valid', this);
36734     },
36735     
36736     markInvalid : function(msg)
36737     {
36738         if(this.allowBlank || this.disabled){
36739             return;
36740         }
36741         
36742         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36743             this.indicatorEl().removeClass('invisible');
36744             this.indicatorEl().addClass('visible');
36745         }
36746         if (Roo.bootstrap.version == 3) {
36747             this.el.removeClass([this.invalidClass, this.validClass]);
36748             this.el.addClass(this.invalidClass);
36749         } else {
36750             this.el.removeClass(['is-invalid','is-valid']);
36751             this.el.addClass(['is-invalid']);
36752         }
36753         
36754         this.fireEvent('invalid', this, msg);
36755         
36756     },
36757     
36758     setValue : function(v, suppressEvent)
36759     {   
36760         if(this.value === v){
36761             return;
36762         }
36763         
36764         this.value = v;
36765         
36766         if(this.rendered){
36767             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36768         }
36769         
36770         Roo.each(this.radioes, function(i){
36771             i.checked = false;
36772             i.el.removeClass('checked');
36773         });
36774         
36775         Roo.each(this.radioes, function(i){
36776             
36777             if(i.value === v || i.value.toString() === v.toString()){
36778                 i.checked = true;
36779                 i.el.addClass('checked');
36780                 
36781                 if(suppressEvent !== true){
36782                     this.fireEvent('check', this, i);
36783                 }
36784                 
36785                 return false;
36786             }
36787             
36788         }, this);
36789         
36790         this.validate();
36791     },
36792     
36793     clearInvalid : function(){
36794         
36795         if(!this.el || this.preventMark){
36796             return;
36797         }
36798         
36799         this.el.removeClass([this.invalidClass]);
36800         
36801         this.fireEvent('valid', this);
36802     }
36803     
36804 });
36805
36806 Roo.apply(Roo.bootstrap.RadioSet, {
36807     
36808     groups: {},
36809     
36810     register : function(set)
36811     {
36812         this.groups[set.name] = set;
36813     },
36814     
36815     get: function(name) 
36816     {
36817         if (typeof(this.groups[name]) == 'undefined') {
36818             return false;
36819         }
36820         
36821         return this.groups[name] ;
36822     }
36823     
36824 });
36825 /*
36826  * Based on:
36827  * Ext JS Library 1.1.1
36828  * Copyright(c) 2006-2007, Ext JS, LLC.
36829  *
36830  * Originally Released Under LGPL - original licence link has changed is not relivant.
36831  *
36832  * Fork - LGPL
36833  * <script type="text/javascript">
36834  */
36835
36836
36837 /**
36838  * @class Roo.bootstrap.SplitBar
36839  * @extends Roo.util.Observable
36840  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36841  * <br><br>
36842  * Usage:
36843  * <pre><code>
36844 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36845                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36846 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36847 split.minSize = 100;
36848 split.maxSize = 600;
36849 split.animate = true;
36850 split.on('moved', splitterMoved);
36851 </code></pre>
36852  * @constructor
36853  * Create a new SplitBar
36854  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36855  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36856  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36857  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36858                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36859                         position of the SplitBar).
36860  */
36861 Roo.bootstrap.SplitBar = function(cfg){
36862     
36863     /** @private */
36864     
36865     //{
36866     //  dragElement : elm
36867     //  resizingElement: el,
36868         // optional..
36869     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36870     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36871         // existingProxy ???
36872     //}
36873     
36874     this.el = Roo.get(cfg.dragElement, true);
36875     this.el.dom.unselectable = "on";
36876     /** @private */
36877     this.resizingEl = Roo.get(cfg.resizingElement, true);
36878
36879     /**
36880      * @private
36881      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36882      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36883      * @type Number
36884      */
36885     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36886     
36887     /**
36888      * The minimum size of the resizing element. (Defaults to 0)
36889      * @type Number
36890      */
36891     this.minSize = 0;
36892     
36893     /**
36894      * The maximum size of the resizing element. (Defaults to 2000)
36895      * @type Number
36896      */
36897     this.maxSize = 2000;
36898     
36899     /**
36900      * Whether to animate the transition to the new size
36901      * @type Boolean
36902      */
36903     this.animate = false;
36904     
36905     /**
36906      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36907      * @type Boolean
36908      */
36909     this.useShim = false;
36910     
36911     /** @private */
36912     this.shim = null;
36913     
36914     if(!cfg.existingProxy){
36915         /** @private */
36916         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36917     }else{
36918         this.proxy = Roo.get(cfg.existingProxy).dom;
36919     }
36920     /** @private */
36921     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36922     
36923     /** @private */
36924     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36925     
36926     /** @private */
36927     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36928     
36929     /** @private */
36930     this.dragSpecs = {};
36931     
36932     /**
36933      * @private The adapter to use to positon and resize elements
36934      */
36935     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36936     this.adapter.init(this);
36937     
36938     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36939         /** @private */
36940         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36941         this.el.addClass("roo-splitbar-h");
36942     }else{
36943         /** @private */
36944         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36945         this.el.addClass("roo-splitbar-v");
36946     }
36947     
36948     this.addEvents({
36949         /**
36950          * @event resize
36951          * Fires when the splitter is moved (alias for {@link #event-moved})
36952          * @param {Roo.bootstrap.SplitBar} this
36953          * @param {Number} newSize the new width or height
36954          */
36955         "resize" : true,
36956         /**
36957          * @event moved
36958          * Fires when the splitter is moved
36959          * @param {Roo.bootstrap.SplitBar} this
36960          * @param {Number} newSize the new width or height
36961          */
36962         "moved" : true,
36963         /**
36964          * @event beforeresize
36965          * Fires before the splitter is dragged
36966          * @param {Roo.bootstrap.SplitBar} this
36967          */
36968         "beforeresize" : true,
36969
36970         "beforeapply" : true
36971     });
36972
36973     Roo.util.Observable.call(this);
36974 };
36975
36976 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36977     onStartProxyDrag : function(x, y){
36978         this.fireEvent("beforeresize", this);
36979         if(!this.overlay){
36980             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36981             o.unselectable();
36982             o.enableDisplayMode("block");
36983             // all splitbars share the same overlay
36984             Roo.bootstrap.SplitBar.prototype.overlay = o;
36985         }
36986         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36987         this.overlay.show();
36988         Roo.get(this.proxy).setDisplayed("block");
36989         var size = this.adapter.getElementSize(this);
36990         this.activeMinSize = this.getMinimumSize();;
36991         this.activeMaxSize = this.getMaximumSize();;
36992         var c1 = size - this.activeMinSize;
36993         var c2 = Math.max(this.activeMaxSize - size, 0);
36994         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36995             this.dd.resetConstraints();
36996             this.dd.setXConstraint(
36997                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36998                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36999             );
37000             this.dd.setYConstraint(0, 0);
37001         }else{
37002             this.dd.resetConstraints();
37003             this.dd.setXConstraint(0, 0);
37004             this.dd.setYConstraint(
37005                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
37006                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
37007             );
37008          }
37009         this.dragSpecs.startSize = size;
37010         this.dragSpecs.startPoint = [x, y];
37011         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
37012     },
37013     
37014     /** 
37015      * @private Called after the drag operation by the DDProxy
37016      */
37017     onEndProxyDrag : function(e){
37018         Roo.get(this.proxy).setDisplayed(false);
37019         var endPoint = Roo.lib.Event.getXY(e);
37020         if(this.overlay){
37021             this.overlay.hide();
37022         }
37023         var newSize;
37024         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37025             newSize = this.dragSpecs.startSize + 
37026                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
37027                     endPoint[0] - this.dragSpecs.startPoint[0] :
37028                     this.dragSpecs.startPoint[0] - endPoint[0]
37029                 );
37030         }else{
37031             newSize = this.dragSpecs.startSize + 
37032                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
37033                     endPoint[1] - this.dragSpecs.startPoint[1] :
37034                     this.dragSpecs.startPoint[1] - endPoint[1]
37035                 );
37036         }
37037         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
37038         if(newSize != this.dragSpecs.startSize){
37039             if(this.fireEvent('beforeapply', this, newSize) !== false){
37040                 this.adapter.setElementSize(this, newSize);
37041                 this.fireEvent("moved", this, newSize);
37042                 this.fireEvent("resize", this, newSize);
37043             }
37044         }
37045     },
37046     
37047     /**
37048      * Get the adapter this SplitBar uses
37049      * @return The adapter object
37050      */
37051     getAdapter : function(){
37052         return this.adapter;
37053     },
37054     
37055     /**
37056      * Set the adapter this SplitBar uses
37057      * @param {Object} adapter A SplitBar adapter object
37058      */
37059     setAdapter : function(adapter){
37060         this.adapter = adapter;
37061         this.adapter.init(this);
37062     },
37063     
37064     /**
37065      * Gets the minimum size for the resizing element
37066      * @return {Number} The minimum size
37067      */
37068     getMinimumSize : function(){
37069         return this.minSize;
37070     },
37071     
37072     /**
37073      * Sets the minimum size for the resizing element
37074      * @param {Number} minSize The minimum size
37075      */
37076     setMinimumSize : function(minSize){
37077         this.minSize = minSize;
37078     },
37079     
37080     /**
37081      * Gets the maximum size for the resizing element
37082      * @return {Number} The maximum size
37083      */
37084     getMaximumSize : function(){
37085         return this.maxSize;
37086     },
37087     
37088     /**
37089      * Sets the maximum size for the resizing element
37090      * @param {Number} maxSize The maximum size
37091      */
37092     setMaximumSize : function(maxSize){
37093         this.maxSize = maxSize;
37094     },
37095     
37096     /**
37097      * Sets the initialize size for the resizing element
37098      * @param {Number} size The initial size
37099      */
37100     setCurrentSize : function(size){
37101         var oldAnimate = this.animate;
37102         this.animate = false;
37103         this.adapter.setElementSize(this, size);
37104         this.animate = oldAnimate;
37105     },
37106     
37107     /**
37108      * Destroy this splitbar. 
37109      * @param {Boolean} removeEl True to remove the element
37110      */
37111     destroy : function(removeEl){
37112         if(this.shim){
37113             this.shim.remove();
37114         }
37115         this.dd.unreg();
37116         this.proxy.parentNode.removeChild(this.proxy);
37117         if(removeEl){
37118             this.el.remove();
37119         }
37120     }
37121 });
37122
37123 /**
37124  * @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.
37125  */
37126 Roo.bootstrap.SplitBar.createProxy = function(dir){
37127     var proxy = new Roo.Element(document.createElement("div"));
37128     proxy.unselectable();
37129     var cls = 'roo-splitbar-proxy';
37130     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37131     document.body.appendChild(proxy.dom);
37132     return proxy.dom;
37133 };
37134
37135 /** 
37136  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37137  * Default Adapter. It assumes the splitter and resizing element are not positioned
37138  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37139  */
37140 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37141 };
37142
37143 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37144     // do nothing for now
37145     init : function(s){
37146     
37147     },
37148     /**
37149      * Called before drag operations to get the current size of the resizing element. 
37150      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37151      */
37152      getElementSize : function(s){
37153         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37154             return s.resizingEl.getWidth();
37155         }else{
37156             return s.resizingEl.getHeight();
37157         }
37158     },
37159     
37160     /**
37161      * Called after drag operations to set the size of the resizing element.
37162      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37163      * @param {Number} newSize The new size to set
37164      * @param {Function} onComplete A function to be invoked when resizing is complete
37165      */
37166     setElementSize : function(s, newSize, onComplete){
37167         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37168             if(!s.animate){
37169                 s.resizingEl.setWidth(newSize);
37170                 if(onComplete){
37171                     onComplete(s, newSize);
37172                 }
37173             }else{
37174                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37175             }
37176         }else{
37177             
37178             if(!s.animate){
37179                 s.resizingEl.setHeight(newSize);
37180                 if(onComplete){
37181                     onComplete(s, newSize);
37182                 }
37183             }else{
37184                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37185             }
37186         }
37187     }
37188 };
37189
37190 /** 
37191  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37192  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37193  * Adapter that  moves the splitter element to align with the resized sizing element. 
37194  * Used with an absolute positioned SplitBar.
37195  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37196  * document.body, make sure you assign an id to the body element.
37197  */
37198 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37199     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37200     this.container = Roo.get(container);
37201 };
37202
37203 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37204     init : function(s){
37205         this.basic.init(s);
37206     },
37207     
37208     getElementSize : function(s){
37209         return this.basic.getElementSize(s);
37210     },
37211     
37212     setElementSize : function(s, newSize, onComplete){
37213         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37214     },
37215     
37216     moveSplitter : function(s){
37217         var yes = Roo.bootstrap.SplitBar;
37218         switch(s.placement){
37219             case yes.LEFT:
37220                 s.el.setX(s.resizingEl.getRight());
37221                 break;
37222             case yes.RIGHT:
37223                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37224                 break;
37225             case yes.TOP:
37226                 s.el.setY(s.resizingEl.getBottom());
37227                 break;
37228             case yes.BOTTOM:
37229                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37230                 break;
37231         }
37232     }
37233 };
37234
37235 /**
37236  * Orientation constant - Create a vertical SplitBar
37237  * @static
37238  * @type Number
37239  */
37240 Roo.bootstrap.SplitBar.VERTICAL = 1;
37241
37242 /**
37243  * Orientation constant - Create a horizontal SplitBar
37244  * @static
37245  * @type Number
37246  */
37247 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37248
37249 /**
37250  * Placement constant - The resizing element is to the left of the splitter element
37251  * @static
37252  * @type Number
37253  */
37254 Roo.bootstrap.SplitBar.LEFT = 1;
37255
37256 /**
37257  * Placement constant - The resizing element is to the right of the splitter element
37258  * @static
37259  * @type Number
37260  */
37261 Roo.bootstrap.SplitBar.RIGHT = 2;
37262
37263 /**
37264  * Placement constant - The resizing element is positioned above the splitter element
37265  * @static
37266  * @type Number
37267  */
37268 Roo.bootstrap.SplitBar.TOP = 3;
37269
37270 /**
37271  * Placement constant - The resizing element is positioned under splitter element
37272  * @static
37273  * @type Number
37274  */
37275 Roo.bootstrap.SplitBar.BOTTOM = 4;
37276 Roo.namespace("Roo.bootstrap.layout");/*
37277  * Based on:
37278  * Ext JS Library 1.1.1
37279  * Copyright(c) 2006-2007, Ext JS, LLC.
37280  *
37281  * Originally Released Under LGPL - original licence link has changed is not relivant.
37282  *
37283  * Fork - LGPL
37284  * <script type="text/javascript">
37285  */
37286
37287 /**
37288  * @class Roo.bootstrap.layout.Manager
37289  * @extends Roo.bootstrap.Component
37290  * Base class for layout managers.
37291  */
37292 Roo.bootstrap.layout.Manager = function(config)
37293 {
37294     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37295
37296
37297
37298
37299
37300     /** false to disable window resize monitoring @type Boolean */
37301     this.monitorWindowResize = true;
37302     this.regions = {};
37303     this.addEvents({
37304         /**
37305          * @event layout
37306          * Fires when a layout is performed.
37307          * @param {Roo.LayoutManager} this
37308          */
37309         "layout" : true,
37310         /**
37311          * @event regionresized
37312          * Fires when the user resizes a region.
37313          * @param {Roo.LayoutRegion} region The resized region
37314          * @param {Number} newSize The new size (width for east/west, height for north/south)
37315          */
37316         "regionresized" : true,
37317         /**
37318          * @event regioncollapsed
37319          * Fires when a region is collapsed.
37320          * @param {Roo.LayoutRegion} region The collapsed region
37321          */
37322         "regioncollapsed" : true,
37323         /**
37324          * @event regionexpanded
37325          * Fires when a region is expanded.
37326          * @param {Roo.LayoutRegion} region The expanded region
37327          */
37328         "regionexpanded" : true
37329     });
37330     this.updating = false;
37331
37332     if (config.el) {
37333         this.el = Roo.get(config.el);
37334         this.initEvents();
37335     }
37336
37337 };
37338
37339 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37340
37341
37342     regions : null,
37343
37344     monitorWindowResize : true,
37345
37346
37347     updating : false,
37348
37349
37350     onRender : function(ct, position)
37351     {
37352         if(!this.el){
37353             this.el = Roo.get(ct);
37354             this.initEvents();
37355         }
37356         //this.fireEvent('render',this);
37357     },
37358
37359
37360     initEvents: function()
37361     {
37362
37363
37364         // ie scrollbar fix
37365         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37366             document.body.scroll = "no";
37367         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37368             this.el.position('relative');
37369         }
37370         this.id = this.el.id;
37371         this.el.addClass("roo-layout-container");
37372         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37373         if(this.el.dom != document.body ) {
37374             this.el.on('resize', this.layout,this);
37375             this.el.on('show', this.layout,this);
37376         }
37377
37378     },
37379
37380     /**
37381      * Returns true if this layout is currently being updated
37382      * @return {Boolean}
37383      */
37384     isUpdating : function(){
37385         return this.updating;
37386     },
37387
37388     /**
37389      * Suspend the LayoutManager from doing auto-layouts while
37390      * making multiple add or remove calls
37391      */
37392     beginUpdate : function(){
37393         this.updating = true;
37394     },
37395
37396     /**
37397      * Restore auto-layouts and optionally disable the manager from performing a layout
37398      * @param {Boolean} noLayout true to disable a layout update
37399      */
37400     endUpdate : function(noLayout){
37401         this.updating = false;
37402         if(!noLayout){
37403             this.layout();
37404         }
37405     },
37406
37407     layout: function(){
37408         // abstract...
37409     },
37410
37411     onRegionResized : function(region, newSize){
37412         this.fireEvent("regionresized", region, newSize);
37413         this.layout();
37414     },
37415
37416     onRegionCollapsed : function(region){
37417         this.fireEvent("regioncollapsed", region);
37418     },
37419
37420     onRegionExpanded : function(region){
37421         this.fireEvent("regionexpanded", region);
37422     },
37423
37424     /**
37425      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37426      * performs box-model adjustments.
37427      * @return {Object} The size as an object {width: (the width), height: (the height)}
37428      */
37429     getViewSize : function()
37430     {
37431         var size;
37432         if(this.el.dom != document.body){
37433             size = this.el.getSize();
37434         }else{
37435             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37436         }
37437         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37438         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37439         return size;
37440     },
37441
37442     /**
37443      * Returns the Element this layout is bound to.
37444      * @return {Roo.Element}
37445      */
37446     getEl : function(){
37447         return this.el;
37448     },
37449
37450     /**
37451      * Returns the specified region.
37452      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37453      * @return {Roo.LayoutRegion}
37454      */
37455     getRegion : function(target){
37456         return this.regions[target.toLowerCase()];
37457     },
37458
37459     onWindowResize : function(){
37460         if(this.monitorWindowResize){
37461             this.layout();
37462         }
37463     }
37464 });
37465 /*
37466  * Based on:
37467  * Ext JS Library 1.1.1
37468  * Copyright(c) 2006-2007, Ext JS, LLC.
37469  *
37470  * Originally Released Under LGPL - original licence link has changed is not relivant.
37471  *
37472  * Fork - LGPL
37473  * <script type="text/javascript">
37474  */
37475 /**
37476  * @class Roo.bootstrap.layout.Border
37477  * @extends Roo.bootstrap.layout.Manager
37478  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37479  * please see: examples/bootstrap/nested.html<br><br>
37480  
37481 <b>The container the layout is rendered into can be either the body element or any other element.
37482 If it is not the body element, the container needs to either be an absolute positioned element,
37483 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37484 the container size if it is not the body element.</b>
37485
37486 * @constructor
37487 * Create a new Border
37488 * @param {Object} config Configuration options
37489  */
37490 Roo.bootstrap.layout.Border = function(config){
37491     config = config || {};
37492     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37493     
37494     
37495     
37496     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37497         if(config[region]){
37498             config[region].region = region;
37499             this.addRegion(config[region]);
37500         }
37501     },this);
37502     
37503 };
37504
37505 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37506
37507 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37508     
37509     parent : false, // this might point to a 'nest' or a ???
37510     
37511     /**
37512      * Creates and adds a new region if it doesn't already exist.
37513      * @param {String} target The target region key (north, south, east, west or center).
37514      * @param {Object} config The regions config object
37515      * @return {BorderLayoutRegion} The new region
37516      */
37517     addRegion : function(config)
37518     {
37519         if(!this.regions[config.region]){
37520             var r = this.factory(config);
37521             this.bindRegion(r);
37522         }
37523         return this.regions[config.region];
37524     },
37525
37526     // private (kinda)
37527     bindRegion : function(r){
37528         this.regions[r.config.region] = r;
37529         
37530         r.on("visibilitychange",    this.layout, this);
37531         r.on("paneladded",          this.layout, this);
37532         r.on("panelremoved",        this.layout, this);
37533         r.on("invalidated",         this.layout, this);
37534         r.on("resized",             this.onRegionResized, this);
37535         r.on("collapsed",           this.onRegionCollapsed, this);
37536         r.on("expanded",            this.onRegionExpanded, this);
37537     },
37538
37539     /**
37540      * Performs a layout update.
37541      */
37542     layout : function()
37543     {
37544         if(this.updating) {
37545             return;
37546         }
37547         
37548         // render all the rebions if they have not been done alreayd?
37549         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37550             if(this.regions[region] && !this.regions[region].bodyEl){
37551                 this.regions[region].onRender(this.el)
37552             }
37553         },this);
37554         
37555         var size = this.getViewSize();
37556         var w = size.width;
37557         var h = size.height;
37558         var centerW = w;
37559         var centerH = h;
37560         var centerY = 0;
37561         var centerX = 0;
37562         //var x = 0, y = 0;
37563
37564         var rs = this.regions;
37565         var north = rs["north"];
37566         var south = rs["south"]; 
37567         var west = rs["west"];
37568         var east = rs["east"];
37569         var center = rs["center"];
37570         //if(this.hideOnLayout){ // not supported anymore
37571             //c.el.setStyle("display", "none");
37572         //}
37573         if(north && north.isVisible()){
37574             var b = north.getBox();
37575             var m = north.getMargins();
37576             b.width = w - (m.left+m.right);
37577             b.x = m.left;
37578             b.y = m.top;
37579             centerY = b.height + b.y + m.bottom;
37580             centerH -= centerY;
37581             north.updateBox(this.safeBox(b));
37582         }
37583         if(south && south.isVisible()){
37584             var b = south.getBox();
37585             var m = south.getMargins();
37586             b.width = w - (m.left+m.right);
37587             b.x = m.left;
37588             var totalHeight = (b.height + m.top + m.bottom);
37589             b.y = h - totalHeight + m.top;
37590             centerH -= totalHeight;
37591             south.updateBox(this.safeBox(b));
37592         }
37593         if(west && west.isVisible()){
37594             var b = west.getBox();
37595             var m = west.getMargins();
37596             b.height = centerH - (m.top+m.bottom);
37597             b.x = m.left;
37598             b.y = centerY + m.top;
37599             var totalWidth = (b.width + m.left + m.right);
37600             centerX += totalWidth;
37601             centerW -= totalWidth;
37602             west.updateBox(this.safeBox(b));
37603         }
37604         if(east && east.isVisible()){
37605             var b = east.getBox();
37606             var m = east.getMargins();
37607             b.height = centerH - (m.top+m.bottom);
37608             var totalWidth = (b.width + m.left + m.right);
37609             b.x = w - totalWidth + m.left;
37610             b.y = centerY + m.top;
37611             centerW -= totalWidth;
37612             east.updateBox(this.safeBox(b));
37613         }
37614         if(center){
37615             var m = center.getMargins();
37616             var centerBox = {
37617                 x: centerX + m.left,
37618                 y: centerY + m.top,
37619                 width: centerW - (m.left+m.right),
37620                 height: centerH - (m.top+m.bottom)
37621             };
37622             //if(this.hideOnLayout){
37623                 //center.el.setStyle("display", "block");
37624             //}
37625             center.updateBox(this.safeBox(centerBox));
37626         }
37627         this.el.repaint();
37628         this.fireEvent("layout", this);
37629     },
37630
37631     // private
37632     safeBox : function(box){
37633         box.width = Math.max(0, box.width);
37634         box.height = Math.max(0, box.height);
37635         return box;
37636     },
37637
37638     /**
37639      * Adds a ContentPanel (or subclass) to this layout.
37640      * @param {String} target The target region key (north, south, east, west or center).
37641      * @param {Roo.ContentPanel} panel The panel to add
37642      * @return {Roo.ContentPanel} The added panel
37643      */
37644     add : function(target, panel){
37645          
37646         target = target.toLowerCase();
37647         return this.regions[target].add(panel);
37648     },
37649
37650     /**
37651      * Remove a ContentPanel (or subclass) to this layout.
37652      * @param {String} target The target region key (north, south, east, west or center).
37653      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37654      * @return {Roo.ContentPanel} The removed panel
37655      */
37656     remove : function(target, panel){
37657         target = target.toLowerCase();
37658         return this.regions[target].remove(panel);
37659     },
37660
37661     /**
37662      * Searches all regions for a panel with the specified id
37663      * @param {String} panelId
37664      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37665      */
37666     findPanel : function(panelId){
37667         var rs = this.regions;
37668         for(var target in rs){
37669             if(typeof rs[target] != "function"){
37670                 var p = rs[target].getPanel(panelId);
37671                 if(p){
37672                     return p;
37673                 }
37674             }
37675         }
37676         return null;
37677     },
37678
37679     /**
37680      * Searches all regions for a panel with the specified id and activates (shows) it.
37681      * @param {String/ContentPanel} panelId The panels id or the panel itself
37682      * @return {Roo.ContentPanel} The shown panel or null
37683      */
37684     showPanel : function(panelId) {
37685       var rs = this.regions;
37686       for(var target in rs){
37687          var r = rs[target];
37688          if(typeof r != "function"){
37689             if(r.hasPanel(panelId)){
37690                return r.showPanel(panelId);
37691             }
37692          }
37693       }
37694       return null;
37695    },
37696
37697    /**
37698      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37699      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37700      */
37701    /*
37702     restoreState : function(provider){
37703         if(!provider){
37704             provider = Roo.state.Manager;
37705         }
37706         var sm = new Roo.LayoutStateManager();
37707         sm.init(this, provider);
37708     },
37709 */
37710  
37711  
37712     /**
37713      * Adds a xtype elements to the layout.
37714      * <pre><code>
37715
37716 layout.addxtype({
37717        xtype : 'ContentPanel',
37718        region: 'west',
37719        items: [ .... ]
37720    }
37721 );
37722
37723 layout.addxtype({
37724         xtype : 'NestedLayoutPanel',
37725         region: 'west',
37726         layout: {
37727            center: { },
37728            west: { }   
37729         },
37730         items : [ ... list of content panels or nested layout panels.. ]
37731    }
37732 );
37733 </code></pre>
37734      * @param {Object} cfg Xtype definition of item to add.
37735      */
37736     addxtype : function(cfg)
37737     {
37738         // basically accepts a pannel...
37739         // can accept a layout region..!?!?
37740         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37741         
37742         
37743         // theory?  children can only be panels??
37744         
37745         //if (!cfg.xtype.match(/Panel$/)) {
37746         //    return false;
37747         //}
37748         var ret = false;
37749         
37750         if (typeof(cfg.region) == 'undefined') {
37751             Roo.log("Failed to add Panel, region was not set");
37752             Roo.log(cfg);
37753             return false;
37754         }
37755         var region = cfg.region;
37756         delete cfg.region;
37757         
37758           
37759         var xitems = [];
37760         if (cfg.items) {
37761             xitems = cfg.items;
37762             delete cfg.items;
37763         }
37764         var nb = false;
37765         
37766         if ( region == 'center') {
37767             Roo.log("Center: " + cfg.title);
37768         }
37769         
37770         
37771         switch(cfg.xtype) 
37772         {
37773             case 'Content':  // ContentPanel (el, cfg)
37774             case 'Scroll':  // ContentPanel (el, cfg)
37775             case 'View': 
37776                 cfg.autoCreate = cfg.autoCreate || true;
37777                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37778                 //} else {
37779                 //    var el = this.el.createChild();
37780                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37781                 //}
37782                 
37783                 this.add(region, ret);
37784                 break;
37785             
37786             /*
37787             case 'TreePanel': // our new panel!
37788                 cfg.el = this.el.createChild();
37789                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37790                 this.add(region, ret);
37791                 break;
37792             */
37793             
37794             case 'Nest': 
37795                 // create a new Layout (which is  a Border Layout...
37796                 
37797                 var clayout = cfg.layout;
37798                 clayout.el  = this.el.createChild();
37799                 clayout.items   = clayout.items  || [];
37800                 
37801                 delete cfg.layout;
37802                 
37803                 // replace this exitems with the clayout ones..
37804                 xitems = clayout.items;
37805                  
37806                 // force background off if it's in center...
37807                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37808                     cfg.background = false;
37809                 }
37810                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37811                 
37812                 
37813                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37814                 //console.log('adding nested layout panel '  + cfg.toSource());
37815                 this.add(region, ret);
37816                 nb = {}; /// find first...
37817                 break;
37818             
37819             case 'Grid':
37820                 
37821                 // needs grid and region
37822                 
37823                 //var el = this.getRegion(region).el.createChild();
37824                 /*
37825                  *var el = this.el.createChild();
37826                 // create the grid first...
37827                 cfg.grid.container = el;
37828                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37829                 */
37830                 
37831                 if (region == 'center' && this.active ) {
37832                     cfg.background = false;
37833                 }
37834                 
37835                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37836                 
37837                 this.add(region, ret);
37838                 /*
37839                 if (cfg.background) {
37840                     // render grid on panel activation (if panel background)
37841                     ret.on('activate', function(gp) {
37842                         if (!gp.grid.rendered) {
37843                     //        gp.grid.render(el);
37844                         }
37845                     });
37846                 } else {
37847                   //  cfg.grid.render(el);
37848                 }
37849                 */
37850                 break;
37851            
37852            
37853             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37854                 // it was the old xcomponent building that caused this before.
37855                 // espeically if border is the top element in the tree.
37856                 ret = this;
37857                 break; 
37858                 
37859                     
37860                 
37861                 
37862                 
37863             default:
37864                 /*
37865                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37866                     
37867                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37868                     this.add(region, ret);
37869                 } else {
37870                 */
37871                     Roo.log(cfg);
37872                     throw "Can not add '" + cfg.xtype + "' to Border";
37873                     return null;
37874              
37875                                 
37876              
37877         }
37878         this.beginUpdate();
37879         // add children..
37880         var region = '';
37881         var abn = {};
37882         Roo.each(xitems, function(i)  {
37883             region = nb && i.region ? i.region : false;
37884             
37885             var add = ret.addxtype(i);
37886            
37887             if (region) {
37888                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37889                 if (!i.background) {
37890                     abn[region] = nb[region] ;
37891                 }
37892             }
37893             
37894         });
37895         this.endUpdate();
37896
37897         // make the last non-background panel active..
37898         //if (nb) { Roo.log(abn); }
37899         if (nb) {
37900             
37901             for(var r in abn) {
37902                 region = this.getRegion(r);
37903                 if (region) {
37904                     // tried using nb[r], but it does not work..
37905                      
37906                     region.showPanel(abn[r]);
37907                    
37908                 }
37909             }
37910         }
37911         return ret;
37912         
37913     },
37914     
37915     
37916 // private
37917     factory : function(cfg)
37918     {
37919         
37920         var validRegions = Roo.bootstrap.layout.Border.regions;
37921
37922         var target = cfg.region;
37923         cfg.mgr = this;
37924         
37925         var r = Roo.bootstrap.layout;
37926         Roo.log(target);
37927         switch(target){
37928             case "north":
37929                 return new r.North(cfg);
37930             case "south":
37931                 return new r.South(cfg);
37932             case "east":
37933                 return new r.East(cfg);
37934             case "west":
37935                 return new r.West(cfg);
37936             case "center":
37937                 return new r.Center(cfg);
37938         }
37939         throw 'Layout region "'+target+'" not supported.';
37940     }
37941     
37942     
37943 });
37944  /*
37945  * Based on:
37946  * Ext JS Library 1.1.1
37947  * Copyright(c) 2006-2007, Ext JS, LLC.
37948  *
37949  * Originally Released Under LGPL - original licence link has changed is not relivant.
37950  *
37951  * Fork - LGPL
37952  * <script type="text/javascript">
37953  */
37954  
37955 /**
37956  * @class Roo.bootstrap.layout.Basic
37957  * @extends Roo.util.Observable
37958  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37959  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37960  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37961  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37962  * @cfg {string}   region  the region that it inhabits..
37963  * @cfg {bool}   skipConfig skip config?
37964  * 
37965
37966  */
37967 Roo.bootstrap.layout.Basic = function(config){
37968     
37969     this.mgr = config.mgr;
37970     
37971     this.position = config.region;
37972     
37973     var skipConfig = config.skipConfig;
37974     
37975     this.events = {
37976         /**
37977          * @scope Roo.BasicLayoutRegion
37978          */
37979         
37980         /**
37981          * @event beforeremove
37982          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37983          * @param {Roo.LayoutRegion} this
37984          * @param {Roo.ContentPanel} panel The panel
37985          * @param {Object} e The cancel event object
37986          */
37987         "beforeremove" : true,
37988         /**
37989          * @event invalidated
37990          * Fires when the layout for this region is changed.
37991          * @param {Roo.LayoutRegion} this
37992          */
37993         "invalidated" : true,
37994         /**
37995          * @event visibilitychange
37996          * Fires when this region is shown or hidden 
37997          * @param {Roo.LayoutRegion} this
37998          * @param {Boolean} visibility true or false
37999          */
38000         "visibilitychange" : true,
38001         /**
38002          * @event paneladded
38003          * Fires when a panel is added. 
38004          * @param {Roo.LayoutRegion} this
38005          * @param {Roo.ContentPanel} panel The panel
38006          */
38007         "paneladded" : true,
38008         /**
38009          * @event panelremoved
38010          * Fires when a panel is removed. 
38011          * @param {Roo.LayoutRegion} this
38012          * @param {Roo.ContentPanel} panel The panel
38013          */
38014         "panelremoved" : true,
38015         /**
38016          * @event beforecollapse
38017          * Fires when this region before collapse.
38018          * @param {Roo.LayoutRegion} this
38019          */
38020         "beforecollapse" : true,
38021         /**
38022          * @event collapsed
38023          * Fires when this region is collapsed.
38024          * @param {Roo.LayoutRegion} this
38025          */
38026         "collapsed" : true,
38027         /**
38028          * @event expanded
38029          * Fires when this region is expanded.
38030          * @param {Roo.LayoutRegion} this
38031          */
38032         "expanded" : true,
38033         /**
38034          * @event slideshow
38035          * Fires when this region is slid into view.
38036          * @param {Roo.LayoutRegion} this
38037          */
38038         "slideshow" : true,
38039         /**
38040          * @event slidehide
38041          * Fires when this region slides out of view. 
38042          * @param {Roo.LayoutRegion} this
38043          */
38044         "slidehide" : true,
38045         /**
38046          * @event panelactivated
38047          * Fires when a panel is activated. 
38048          * @param {Roo.LayoutRegion} this
38049          * @param {Roo.ContentPanel} panel The activated panel
38050          */
38051         "panelactivated" : true,
38052         /**
38053          * @event resized
38054          * Fires when the user resizes this region. 
38055          * @param {Roo.LayoutRegion} this
38056          * @param {Number} newSize The new size (width for east/west, height for north/south)
38057          */
38058         "resized" : true
38059     };
38060     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38061     this.panels = new Roo.util.MixedCollection();
38062     this.panels.getKey = this.getPanelId.createDelegate(this);
38063     this.box = null;
38064     this.activePanel = null;
38065     // ensure listeners are added...
38066     
38067     if (config.listeners || config.events) {
38068         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38069             listeners : config.listeners || {},
38070             events : config.events || {}
38071         });
38072     }
38073     
38074     if(skipConfig !== true){
38075         this.applyConfig(config);
38076     }
38077 };
38078
38079 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38080 {
38081     getPanelId : function(p){
38082         return p.getId();
38083     },
38084     
38085     applyConfig : function(config){
38086         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38087         this.config = config;
38088         
38089     },
38090     
38091     /**
38092      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38093      * the width, for horizontal (north, south) the height.
38094      * @param {Number} newSize The new width or height
38095      */
38096     resizeTo : function(newSize){
38097         var el = this.el ? this.el :
38098                  (this.activePanel ? this.activePanel.getEl() : null);
38099         if(el){
38100             switch(this.position){
38101                 case "east":
38102                 case "west":
38103                     el.setWidth(newSize);
38104                     this.fireEvent("resized", this, newSize);
38105                 break;
38106                 case "north":
38107                 case "south":
38108                     el.setHeight(newSize);
38109                     this.fireEvent("resized", this, newSize);
38110                 break;                
38111             }
38112         }
38113     },
38114     
38115     getBox : function(){
38116         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38117     },
38118     
38119     getMargins : function(){
38120         return this.margins;
38121     },
38122     
38123     updateBox : function(box){
38124         this.box = box;
38125         var el = this.activePanel.getEl();
38126         el.dom.style.left = box.x + "px";
38127         el.dom.style.top = box.y + "px";
38128         this.activePanel.setSize(box.width, box.height);
38129     },
38130     
38131     /**
38132      * Returns the container element for this region.
38133      * @return {Roo.Element}
38134      */
38135     getEl : function(){
38136         return this.activePanel;
38137     },
38138     
38139     /**
38140      * Returns true if this region is currently visible.
38141      * @return {Boolean}
38142      */
38143     isVisible : function(){
38144         return this.activePanel ? true : false;
38145     },
38146     
38147     setActivePanel : function(panel){
38148         panel = this.getPanel(panel);
38149         if(this.activePanel && this.activePanel != panel){
38150             this.activePanel.setActiveState(false);
38151             this.activePanel.getEl().setLeftTop(-10000,-10000);
38152         }
38153         this.activePanel = panel;
38154         panel.setActiveState(true);
38155         if(this.box){
38156             panel.setSize(this.box.width, this.box.height);
38157         }
38158         this.fireEvent("panelactivated", this, panel);
38159         this.fireEvent("invalidated");
38160     },
38161     
38162     /**
38163      * Show the specified panel.
38164      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38165      * @return {Roo.ContentPanel} The shown panel or null
38166      */
38167     showPanel : function(panel){
38168         panel = this.getPanel(panel);
38169         if(panel){
38170             this.setActivePanel(panel);
38171         }
38172         return panel;
38173     },
38174     
38175     /**
38176      * Get the active panel for this region.
38177      * @return {Roo.ContentPanel} The active panel or null
38178      */
38179     getActivePanel : function(){
38180         return this.activePanel;
38181     },
38182     
38183     /**
38184      * Add the passed ContentPanel(s)
38185      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38186      * @return {Roo.ContentPanel} The panel added (if only one was added)
38187      */
38188     add : function(panel){
38189         if(arguments.length > 1){
38190             for(var i = 0, len = arguments.length; i < len; i++) {
38191                 this.add(arguments[i]);
38192             }
38193             return null;
38194         }
38195         if(this.hasPanel(panel)){
38196             this.showPanel(panel);
38197             return panel;
38198         }
38199         var el = panel.getEl();
38200         if(el.dom.parentNode != this.mgr.el.dom){
38201             this.mgr.el.dom.appendChild(el.dom);
38202         }
38203         if(panel.setRegion){
38204             panel.setRegion(this);
38205         }
38206         this.panels.add(panel);
38207         el.setStyle("position", "absolute");
38208         if(!panel.background){
38209             this.setActivePanel(panel);
38210             if(this.config.initialSize && this.panels.getCount()==1){
38211                 this.resizeTo(this.config.initialSize);
38212             }
38213         }
38214         this.fireEvent("paneladded", this, panel);
38215         return panel;
38216     },
38217     
38218     /**
38219      * Returns true if the panel is in this region.
38220      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38221      * @return {Boolean}
38222      */
38223     hasPanel : function(panel){
38224         if(typeof panel == "object"){ // must be panel obj
38225             panel = panel.getId();
38226         }
38227         return this.getPanel(panel) ? true : false;
38228     },
38229     
38230     /**
38231      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38232      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38233      * @param {Boolean} preservePanel Overrides the config preservePanel option
38234      * @return {Roo.ContentPanel} The panel that was removed
38235      */
38236     remove : function(panel, preservePanel){
38237         panel = this.getPanel(panel);
38238         if(!panel){
38239             return null;
38240         }
38241         var e = {};
38242         this.fireEvent("beforeremove", this, panel, e);
38243         if(e.cancel === true){
38244             return null;
38245         }
38246         var panelId = panel.getId();
38247         this.panels.removeKey(panelId);
38248         return panel;
38249     },
38250     
38251     /**
38252      * Returns the panel specified or null if it's not in this region.
38253      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38254      * @return {Roo.ContentPanel}
38255      */
38256     getPanel : function(id){
38257         if(typeof id == "object"){ // must be panel obj
38258             return id;
38259         }
38260         return this.panels.get(id);
38261     },
38262     
38263     /**
38264      * Returns this regions position (north/south/east/west/center).
38265      * @return {String} 
38266      */
38267     getPosition: function(){
38268         return this.position;    
38269     }
38270 });/*
38271  * Based on:
38272  * Ext JS Library 1.1.1
38273  * Copyright(c) 2006-2007, Ext JS, LLC.
38274  *
38275  * Originally Released Under LGPL - original licence link has changed is not relivant.
38276  *
38277  * Fork - LGPL
38278  * <script type="text/javascript">
38279  */
38280  
38281 /**
38282  * @class Roo.bootstrap.layout.Region
38283  * @extends Roo.bootstrap.layout.Basic
38284  * This class represents a region in a layout manager.
38285  
38286  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38287  * @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})
38288  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38289  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38290  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38291  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38292  * @cfg {String}    title           The title for the region (overrides panel titles)
38293  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38294  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38295  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38296  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38297  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38298  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38299  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38300  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38301  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38302  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38303
38304  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38305  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38306  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38307  * @cfg {Number}    width           For East/West panels
38308  * @cfg {Number}    height          For North/South panels
38309  * @cfg {Boolean}   split           To show the splitter
38310  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38311  * 
38312  * @cfg {string}   cls             Extra CSS classes to add to region
38313  * 
38314  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38315  * @cfg {string}   region  the region that it inhabits..
38316  *
38317
38318  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38319  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38320
38321  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38322  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38323  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38324  */
38325 Roo.bootstrap.layout.Region = function(config)
38326 {
38327     this.applyConfig(config);
38328
38329     var mgr = config.mgr;
38330     var pos = config.region;
38331     config.skipConfig = true;
38332     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38333     
38334     if (mgr.el) {
38335         this.onRender(mgr.el);   
38336     }
38337      
38338     this.visible = true;
38339     this.collapsed = false;
38340     this.unrendered_panels = [];
38341 };
38342
38343 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38344
38345     position: '', // set by wrapper (eg. north/south etc..)
38346     unrendered_panels : null,  // unrendered panels.
38347     
38348     tabPosition : false,
38349     
38350     mgr: false, // points to 'Border'
38351     
38352     
38353     createBody : function(){
38354         /** This region's body element 
38355         * @type Roo.Element */
38356         this.bodyEl = this.el.createChild({
38357                 tag: "div",
38358                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38359         });
38360     },
38361
38362     onRender: function(ctr, pos)
38363     {
38364         var dh = Roo.DomHelper;
38365         /** This region's container element 
38366         * @type Roo.Element */
38367         this.el = dh.append(ctr.dom, {
38368                 tag: "div",
38369                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38370             }, true);
38371         /** This region's title element 
38372         * @type Roo.Element */
38373     
38374         this.titleEl = dh.append(this.el.dom,  {
38375                 tag: "div",
38376                 unselectable: "on",
38377                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38378                 children:[
38379                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38380                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38381                 ]
38382             }, true);
38383         
38384         this.titleEl.enableDisplayMode();
38385         /** This region's title text element 
38386         * @type HTMLElement */
38387         this.titleTextEl = this.titleEl.dom.firstChild;
38388         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38389         /*
38390         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38391         this.closeBtn.enableDisplayMode();
38392         this.closeBtn.on("click", this.closeClicked, this);
38393         this.closeBtn.hide();
38394     */
38395         this.createBody(this.config);
38396         if(this.config.hideWhenEmpty){
38397             this.hide();
38398             this.on("paneladded", this.validateVisibility, this);
38399             this.on("panelremoved", this.validateVisibility, this);
38400         }
38401         if(this.autoScroll){
38402             this.bodyEl.setStyle("overflow", "auto");
38403         }else{
38404             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38405         }
38406         //if(c.titlebar !== false){
38407             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38408                 this.titleEl.hide();
38409             }else{
38410                 this.titleEl.show();
38411                 if(this.config.title){
38412                     this.titleTextEl.innerHTML = this.config.title;
38413                 }
38414             }
38415         //}
38416         if(this.config.collapsed){
38417             this.collapse(true);
38418         }
38419         if(this.config.hidden){
38420             this.hide();
38421         }
38422         
38423         if (this.unrendered_panels && this.unrendered_panels.length) {
38424             for (var i =0;i< this.unrendered_panels.length; i++) {
38425                 this.add(this.unrendered_panels[i]);
38426             }
38427             this.unrendered_panels = null;
38428             
38429         }
38430         
38431     },
38432     
38433     applyConfig : function(c)
38434     {
38435         /*
38436          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38437             var dh = Roo.DomHelper;
38438             if(c.titlebar !== false){
38439                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38440                 this.collapseBtn.on("click", this.collapse, this);
38441                 this.collapseBtn.enableDisplayMode();
38442                 /*
38443                 if(c.showPin === true || this.showPin){
38444                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38445                     this.stickBtn.enableDisplayMode();
38446                     this.stickBtn.on("click", this.expand, this);
38447                     this.stickBtn.hide();
38448                 }
38449                 
38450             }
38451             */
38452             /** This region's collapsed element
38453             * @type Roo.Element */
38454             /*
38455              *
38456             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38457                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38458             ]}, true);
38459             
38460             if(c.floatable !== false){
38461                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38462                this.collapsedEl.on("click", this.collapseClick, this);
38463             }
38464
38465             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38466                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38467                    id: "message", unselectable: "on", style:{"float":"left"}});
38468                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38469              }
38470             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38471             this.expandBtn.on("click", this.expand, this);
38472             
38473         }
38474         
38475         if(this.collapseBtn){
38476             this.collapseBtn.setVisible(c.collapsible == true);
38477         }
38478         
38479         this.cmargins = c.cmargins || this.cmargins ||
38480                          (this.position == "west" || this.position == "east" ?
38481                              {top: 0, left: 2, right:2, bottom: 0} :
38482                              {top: 2, left: 0, right:0, bottom: 2});
38483         */
38484         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38485         
38486         
38487         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38488         
38489         this.autoScroll = c.autoScroll || false;
38490         
38491         
38492        
38493         
38494         this.duration = c.duration || .30;
38495         this.slideDuration = c.slideDuration || .45;
38496         this.config = c;
38497        
38498     },
38499     /**
38500      * Returns true if this region is currently visible.
38501      * @return {Boolean}
38502      */
38503     isVisible : function(){
38504         return this.visible;
38505     },
38506
38507     /**
38508      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38509      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38510      */
38511     //setCollapsedTitle : function(title){
38512     //    title = title || "&#160;";
38513      //   if(this.collapsedTitleTextEl){
38514       //      this.collapsedTitleTextEl.innerHTML = title;
38515        // }
38516     //},
38517
38518     getBox : function(){
38519         var b;
38520       //  if(!this.collapsed){
38521             b = this.el.getBox(false, true);
38522        // }else{
38523           //  b = this.collapsedEl.getBox(false, true);
38524         //}
38525         return b;
38526     },
38527
38528     getMargins : function(){
38529         return this.margins;
38530         //return this.collapsed ? this.cmargins : this.margins;
38531     },
38532 /*
38533     highlight : function(){
38534         this.el.addClass("x-layout-panel-dragover");
38535     },
38536
38537     unhighlight : function(){
38538         this.el.removeClass("x-layout-panel-dragover");
38539     },
38540 */
38541     updateBox : function(box)
38542     {
38543         if (!this.bodyEl) {
38544             return; // not rendered yet..
38545         }
38546         
38547         this.box = box;
38548         if(!this.collapsed){
38549             this.el.dom.style.left = box.x + "px";
38550             this.el.dom.style.top = box.y + "px";
38551             this.updateBody(box.width, box.height);
38552         }else{
38553             this.collapsedEl.dom.style.left = box.x + "px";
38554             this.collapsedEl.dom.style.top = box.y + "px";
38555             this.collapsedEl.setSize(box.width, box.height);
38556         }
38557         if(this.tabs){
38558             this.tabs.autoSizeTabs();
38559         }
38560     },
38561
38562     updateBody : function(w, h)
38563     {
38564         if(w !== null){
38565             this.el.setWidth(w);
38566             w -= this.el.getBorderWidth("rl");
38567             if(this.config.adjustments){
38568                 w += this.config.adjustments[0];
38569             }
38570         }
38571         if(h !== null && h > 0){
38572             this.el.setHeight(h);
38573             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38574             h -= this.el.getBorderWidth("tb");
38575             if(this.config.adjustments){
38576                 h += this.config.adjustments[1];
38577             }
38578             this.bodyEl.setHeight(h);
38579             if(this.tabs){
38580                 h = this.tabs.syncHeight(h);
38581             }
38582         }
38583         if(this.panelSize){
38584             w = w !== null ? w : this.panelSize.width;
38585             h = h !== null ? h : this.panelSize.height;
38586         }
38587         if(this.activePanel){
38588             var el = this.activePanel.getEl();
38589             w = w !== null ? w : el.getWidth();
38590             h = h !== null ? h : el.getHeight();
38591             this.panelSize = {width: w, height: h};
38592             this.activePanel.setSize(w, h);
38593         }
38594         if(Roo.isIE && this.tabs){
38595             this.tabs.el.repaint();
38596         }
38597     },
38598
38599     /**
38600      * Returns the container element for this region.
38601      * @return {Roo.Element}
38602      */
38603     getEl : function(){
38604         return this.el;
38605     },
38606
38607     /**
38608      * Hides this region.
38609      */
38610     hide : function(){
38611         //if(!this.collapsed){
38612             this.el.dom.style.left = "-2000px";
38613             this.el.hide();
38614         //}else{
38615          //   this.collapsedEl.dom.style.left = "-2000px";
38616          //   this.collapsedEl.hide();
38617        // }
38618         this.visible = false;
38619         this.fireEvent("visibilitychange", this, false);
38620     },
38621
38622     /**
38623      * Shows this region if it was previously hidden.
38624      */
38625     show : function(){
38626         //if(!this.collapsed){
38627             this.el.show();
38628         //}else{
38629         //    this.collapsedEl.show();
38630        // }
38631         this.visible = true;
38632         this.fireEvent("visibilitychange", this, true);
38633     },
38634 /*
38635     closeClicked : function(){
38636         if(this.activePanel){
38637             this.remove(this.activePanel);
38638         }
38639     },
38640
38641     collapseClick : function(e){
38642         if(this.isSlid){
38643            e.stopPropagation();
38644            this.slideIn();
38645         }else{
38646            e.stopPropagation();
38647            this.slideOut();
38648         }
38649     },
38650 */
38651     /**
38652      * Collapses this region.
38653      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38654      */
38655     /*
38656     collapse : function(skipAnim, skipCheck = false){
38657         if(this.collapsed) {
38658             return;
38659         }
38660         
38661         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38662             
38663             this.collapsed = true;
38664             if(this.split){
38665                 this.split.el.hide();
38666             }
38667             if(this.config.animate && skipAnim !== true){
38668                 this.fireEvent("invalidated", this);
38669                 this.animateCollapse();
38670             }else{
38671                 this.el.setLocation(-20000,-20000);
38672                 this.el.hide();
38673                 this.collapsedEl.show();
38674                 this.fireEvent("collapsed", this);
38675                 this.fireEvent("invalidated", this);
38676             }
38677         }
38678         
38679     },
38680 */
38681     animateCollapse : function(){
38682         // overridden
38683     },
38684
38685     /**
38686      * Expands this region if it was previously collapsed.
38687      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38688      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38689      */
38690     /*
38691     expand : function(e, skipAnim){
38692         if(e) {
38693             e.stopPropagation();
38694         }
38695         if(!this.collapsed || this.el.hasActiveFx()) {
38696             return;
38697         }
38698         if(this.isSlid){
38699             this.afterSlideIn();
38700             skipAnim = true;
38701         }
38702         this.collapsed = false;
38703         if(this.config.animate && skipAnim !== true){
38704             this.animateExpand();
38705         }else{
38706             this.el.show();
38707             if(this.split){
38708                 this.split.el.show();
38709             }
38710             this.collapsedEl.setLocation(-2000,-2000);
38711             this.collapsedEl.hide();
38712             this.fireEvent("invalidated", this);
38713             this.fireEvent("expanded", this);
38714         }
38715     },
38716 */
38717     animateExpand : function(){
38718         // overridden
38719     },
38720
38721     initTabs : function()
38722     {
38723         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38724         
38725         var ts = new Roo.bootstrap.panel.Tabs({
38726             el: this.bodyEl.dom,
38727             region : this,
38728             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38729             disableTooltips: this.config.disableTabTips,
38730             toolbar : this.config.toolbar
38731         });
38732         
38733         if(this.config.hideTabs){
38734             ts.stripWrap.setDisplayed(false);
38735         }
38736         this.tabs = ts;
38737         ts.resizeTabs = this.config.resizeTabs === true;
38738         ts.minTabWidth = this.config.minTabWidth || 40;
38739         ts.maxTabWidth = this.config.maxTabWidth || 250;
38740         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38741         ts.monitorResize = false;
38742         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38743         ts.bodyEl.addClass('roo-layout-tabs-body');
38744         this.panels.each(this.initPanelAsTab, this);
38745     },
38746
38747     initPanelAsTab : function(panel){
38748         var ti = this.tabs.addTab(
38749             panel.getEl().id,
38750             panel.getTitle(),
38751             null,
38752             this.config.closeOnTab && panel.isClosable(),
38753             panel.tpl
38754         );
38755         if(panel.tabTip !== undefined){
38756             ti.setTooltip(panel.tabTip);
38757         }
38758         ti.on("activate", function(){
38759               this.setActivePanel(panel);
38760         }, this);
38761         
38762         if(this.config.closeOnTab){
38763             ti.on("beforeclose", function(t, e){
38764                 e.cancel = true;
38765                 this.remove(panel);
38766             }, this);
38767         }
38768         
38769         panel.tabItem = ti;
38770         
38771         return ti;
38772     },
38773
38774     updatePanelTitle : function(panel, title)
38775     {
38776         if(this.activePanel == panel){
38777             this.updateTitle(title);
38778         }
38779         if(this.tabs){
38780             var ti = this.tabs.getTab(panel.getEl().id);
38781             ti.setText(title);
38782             if(panel.tabTip !== undefined){
38783                 ti.setTooltip(panel.tabTip);
38784             }
38785         }
38786     },
38787
38788     updateTitle : function(title){
38789         if(this.titleTextEl && !this.config.title){
38790             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38791         }
38792     },
38793
38794     setActivePanel : function(panel)
38795     {
38796         panel = this.getPanel(panel);
38797         if(this.activePanel && this.activePanel != panel){
38798             if(this.activePanel.setActiveState(false) === false){
38799                 return;
38800             }
38801         }
38802         this.activePanel = panel;
38803         panel.setActiveState(true);
38804         if(this.panelSize){
38805             panel.setSize(this.panelSize.width, this.panelSize.height);
38806         }
38807         if(this.closeBtn){
38808             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38809         }
38810         this.updateTitle(panel.getTitle());
38811         if(this.tabs){
38812             this.fireEvent("invalidated", this);
38813         }
38814         this.fireEvent("panelactivated", this, panel);
38815     },
38816
38817     /**
38818      * Shows the specified panel.
38819      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38820      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38821      */
38822     showPanel : function(panel)
38823     {
38824         panel = this.getPanel(panel);
38825         if(panel){
38826             if(this.tabs){
38827                 var tab = this.tabs.getTab(panel.getEl().id);
38828                 if(tab.isHidden()){
38829                     this.tabs.unhideTab(tab.id);
38830                 }
38831                 tab.activate();
38832             }else{
38833                 this.setActivePanel(panel);
38834             }
38835         }
38836         return panel;
38837     },
38838
38839     /**
38840      * Get the active panel for this region.
38841      * @return {Roo.ContentPanel} The active panel or null
38842      */
38843     getActivePanel : function(){
38844         return this.activePanel;
38845     },
38846
38847     validateVisibility : function(){
38848         if(this.panels.getCount() < 1){
38849             this.updateTitle("&#160;");
38850             this.closeBtn.hide();
38851             this.hide();
38852         }else{
38853             if(!this.isVisible()){
38854                 this.show();
38855             }
38856         }
38857     },
38858
38859     /**
38860      * Adds the passed ContentPanel(s) to this region.
38861      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38862      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38863      */
38864     add : function(panel)
38865     {
38866         if(arguments.length > 1){
38867             for(var i = 0, len = arguments.length; i < len; i++) {
38868                 this.add(arguments[i]);
38869             }
38870             return null;
38871         }
38872         
38873         // if we have not been rendered yet, then we can not really do much of this..
38874         if (!this.bodyEl) {
38875             this.unrendered_panels.push(panel);
38876             return panel;
38877         }
38878         
38879         
38880         
38881         
38882         if(this.hasPanel(panel)){
38883             this.showPanel(panel);
38884             return panel;
38885         }
38886         panel.setRegion(this);
38887         this.panels.add(panel);
38888        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38889             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38890             // and hide them... ???
38891             this.bodyEl.dom.appendChild(panel.getEl().dom);
38892             if(panel.background !== true){
38893                 this.setActivePanel(panel);
38894             }
38895             this.fireEvent("paneladded", this, panel);
38896             return panel;
38897         }
38898         */
38899         if(!this.tabs){
38900             this.initTabs();
38901         }else{
38902             this.initPanelAsTab(panel);
38903         }
38904         
38905         
38906         if(panel.background !== true){
38907             this.tabs.activate(panel.getEl().id);
38908         }
38909         this.fireEvent("paneladded", this, panel);
38910         return panel;
38911     },
38912
38913     /**
38914      * Hides the tab for the specified panel.
38915      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38916      */
38917     hidePanel : function(panel){
38918         if(this.tabs && (panel = this.getPanel(panel))){
38919             this.tabs.hideTab(panel.getEl().id);
38920         }
38921     },
38922
38923     /**
38924      * Unhides the tab for a previously hidden panel.
38925      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38926      */
38927     unhidePanel : function(panel){
38928         if(this.tabs && (panel = this.getPanel(panel))){
38929             this.tabs.unhideTab(panel.getEl().id);
38930         }
38931     },
38932
38933     clearPanels : function(){
38934         while(this.panels.getCount() > 0){
38935              this.remove(this.panels.first());
38936         }
38937     },
38938
38939     /**
38940      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38941      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38942      * @param {Boolean} preservePanel Overrides the config preservePanel option
38943      * @return {Roo.ContentPanel} The panel that was removed
38944      */
38945     remove : function(panel, preservePanel)
38946     {
38947         panel = this.getPanel(panel);
38948         if(!panel){
38949             return null;
38950         }
38951         var e = {};
38952         this.fireEvent("beforeremove", this, panel, e);
38953         if(e.cancel === true){
38954             return null;
38955         }
38956         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38957         var panelId = panel.getId();
38958         this.panels.removeKey(panelId);
38959         if(preservePanel){
38960             document.body.appendChild(panel.getEl().dom);
38961         }
38962         if(this.tabs){
38963             this.tabs.removeTab(panel.getEl().id);
38964         }else if (!preservePanel){
38965             this.bodyEl.dom.removeChild(panel.getEl().dom);
38966         }
38967         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38968             var p = this.panels.first();
38969             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38970             tempEl.appendChild(p.getEl().dom);
38971             this.bodyEl.update("");
38972             this.bodyEl.dom.appendChild(p.getEl().dom);
38973             tempEl = null;
38974             this.updateTitle(p.getTitle());
38975             this.tabs = null;
38976             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38977             this.setActivePanel(p);
38978         }
38979         panel.setRegion(null);
38980         if(this.activePanel == panel){
38981             this.activePanel = null;
38982         }
38983         if(this.config.autoDestroy !== false && preservePanel !== true){
38984             try{panel.destroy();}catch(e){}
38985         }
38986         this.fireEvent("panelremoved", this, panel);
38987         return panel;
38988     },
38989
38990     /**
38991      * Returns the TabPanel component used by this region
38992      * @return {Roo.TabPanel}
38993      */
38994     getTabs : function(){
38995         return this.tabs;
38996     },
38997
38998     createTool : function(parentEl, className){
38999         var btn = Roo.DomHelper.append(parentEl, {
39000             tag: "div",
39001             cls: "x-layout-tools-button",
39002             children: [ {
39003                 tag: "div",
39004                 cls: "roo-layout-tools-button-inner " + className,
39005                 html: "&#160;"
39006             }]
39007         }, true);
39008         btn.addClassOnOver("roo-layout-tools-button-over");
39009         return btn;
39010     }
39011 });/*
39012  * Based on:
39013  * Ext JS Library 1.1.1
39014  * Copyright(c) 2006-2007, Ext JS, LLC.
39015  *
39016  * Originally Released Under LGPL - original licence link has changed is not relivant.
39017  *
39018  * Fork - LGPL
39019  * <script type="text/javascript">
39020  */
39021  
39022
39023
39024 /**
39025  * @class Roo.SplitLayoutRegion
39026  * @extends Roo.LayoutRegion
39027  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
39028  */
39029 Roo.bootstrap.layout.Split = function(config){
39030     this.cursor = config.cursor;
39031     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
39032 };
39033
39034 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
39035 {
39036     splitTip : "Drag to resize.",
39037     collapsibleSplitTip : "Drag to resize. Double click to hide.",
39038     useSplitTips : false,
39039
39040     applyConfig : function(config){
39041         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
39042     },
39043     
39044     onRender : function(ctr,pos) {
39045         
39046         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
39047         if(!this.config.split){
39048             return;
39049         }
39050         if(!this.split){
39051             
39052             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39053                             tag: "div",
39054                             id: this.el.id + "-split",
39055                             cls: "roo-layout-split roo-layout-split-"+this.position,
39056                             html: "&#160;"
39057             });
39058             /** The SplitBar for this region 
39059             * @type Roo.SplitBar */
39060             // does not exist yet...
39061             Roo.log([this.position, this.orientation]);
39062             
39063             this.split = new Roo.bootstrap.SplitBar({
39064                 dragElement : splitEl,
39065                 resizingElement: this.el,
39066                 orientation : this.orientation
39067             });
39068             
39069             this.split.on("moved", this.onSplitMove, this);
39070             this.split.useShim = this.config.useShim === true;
39071             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39072             if(this.useSplitTips){
39073                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39074             }
39075             //if(config.collapsible){
39076             //    this.split.el.on("dblclick", this.collapse,  this);
39077             //}
39078         }
39079         if(typeof this.config.minSize != "undefined"){
39080             this.split.minSize = this.config.minSize;
39081         }
39082         if(typeof this.config.maxSize != "undefined"){
39083             this.split.maxSize = this.config.maxSize;
39084         }
39085         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39086             this.hideSplitter();
39087         }
39088         
39089     },
39090
39091     getHMaxSize : function(){
39092          var cmax = this.config.maxSize || 10000;
39093          var center = this.mgr.getRegion("center");
39094          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39095     },
39096
39097     getVMaxSize : function(){
39098          var cmax = this.config.maxSize || 10000;
39099          var center = this.mgr.getRegion("center");
39100          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39101     },
39102
39103     onSplitMove : function(split, newSize){
39104         this.fireEvent("resized", this, newSize);
39105     },
39106     
39107     /** 
39108      * Returns the {@link Roo.SplitBar} for this region.
39109      * @return {Roo.SplitBar}
39110      */
39111     getSplitBar : function(){
39112         return this.split;
39113     },
39114     
39115     hide : function(){
39116         this.hideSplitter();
39117         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39118     },
39119
39120     hideSplitter : function(){
39121         if(this.split){
39122             this.split.el.setLocation(-2000,-2000);
39123             this.split.el.hide();
39124         }
39125     },
39126
39127     show : function(){
39128         if(this.split){
39129             this.split.el.show();
39130         }
39131         Roo.bootstrap.layout.Split.superclass.show.call(this);
39132     },
39133     
39134     beforeSlide: function(){
39135         if(Roo.isGecko){// firefox overflow auto bug workaround
39136             this.bodyEl.clip();
39137             if(this.tabs) {
39138                 this.tabs.bodyEl.clip();
39139             }
39140             if(this.activePanel){
39141                 this.activePanel.getEl().clip();
39142                 
39143                 if(this.activePanel.beforeSlide){
39144                     this.activePanel.beforeSlide();
39145                 }
39146             }
39147         }
39148     },
39149     
39150     afterSlide : function(){
39151         if(Roo.isGecko){// firefox overflow auto bug workaround
39152             this.bodyEl.unclip();
39153             if(this.tabs) {
39154                 this.tabs.bodyEl.unclip();
39155             }
39156             if(this.activePanel){
39157                 this.activePanel.getEl().unclip();
39158                 if(this.activePanel.afterSlide){
39159                     this.activePanel.afterSlide();
39160                 }
39161             }
39162         }
39163     },
39164
39165     initAutoHide : function(){
39166         if(this.autoHide !== false){
39167             if(!this.autoHideHd){
39168                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39169                 this.autoHideHd = {
39170                     "mouseout": function(e){
39171                         if(!e.within(this.el, true)){
39172                             st.delay(500);
39173                         }
39174                     },
39175                     "mouseover" : function(e){
39176                         st.cancel();
39177                     },
39178                     scope : this
39179                 };
39180             }
39181             this.el.on(this.autoHideHd);
39182         }
39183     },
39184
39185     clearAutoHide : function(){
39186         if(this.autoHide !== false){
39187             this.el.un("mouseout", this.autoHideHd.mouseout);
39188             this.el.un("mouseover", this.autoHideHd.mouseover);
39189         }
39190     },
39191
39192     clearMonitor : function(){
39193         Roo.get(document).un("click", this.slideInIf, this);
39194     },
39195
39196     // these names are backwards but not changed for compat
39197     slideOut : function(){
39198         if(this.isSlid || this.el.hasActiveFx()){
39199             return;
39200         }
39201         this.isSlid = true;
39202         if(this.collapseBtn){
39203             this.collapseBtn.hide();
39204         }
39205         this.closeBtnState = this.closeBtn.getStyle('display');
39206         this.closeBtn.hide();
39207         if(this.stickBtn){
39208             this.stickBtn.show();
39209         }
39210         this.el.show();
39211         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39212         this.beforeSlide();
39213         this.el.setStyle("z-index", 10001);
39214         this.el.slideIn(this.getSlideAnchor(), {
39215             callback: function(){
39216                 this.afterSlide();
39217                 this.initAutoHide();
39218                 Roo.get(document).on("click", this.slideInIf, this);
39219                 this.fireEvent("slideshow", this);
39220             },
39221             scope: this,
39222             block: true
39223         });
39224     },
39225
39226     afterSlideIn : function(){
39227         this.clearAutoHide();
39228         this.isSlid = false;
39229         this.clearMonitor();
39230         this.el.setStyle("z-index", "");
39231         if(this.collapseBtn){
39232             this.collapseBtn.show();
39233         }
39234         this.closeBtn.setStyle('display', this.closeBtnState);
39235         if(this.stickBtn){
39236             this.stickBtn.hide();
39237         }
39238         this.fireEvent("slidehide", this);
39239     },
39240
39241     slideIn : function(cb){
39242         if(!this.isSlid || this.el.hasActiveFx()){
39243             Roo.callback(cb);
39244             return;
39245         }
39246         this.isSlid = false;
39247         this.beforeSlide();
39248         this.el.slideOut(this.getSlideAnchor(), {
39249             callback: function(){
39250                 this.el.setLeftTop(-10000, -10000);
39251                 this.afterSlide();
39252                 this.afterSlideIn();
39253                 Roo.callback(cb);
39254             },
39255             scope: this,
39256             block: true
39257         });
39258     },
39259     
39260     slideInIf : function(e){
39261         if(!e.within(this.el)){
39262             this.slideIn();
39263         }
39264     },
39265
39266     animateCollapse : function(){
39267         this.beforeSlide();
39268         this.el.setStyle("z-index", 20000);
39269         var anchor = this.getSlideAnchor();
39270         this.el.slideOut(anchor, {
39271             callback : function(){
39272                 this.el.setStyle("z-index", "");
39273                 this.collapsedEl.slideIn(anchor, {duration:.3});
39274                 this.afterSlide();
39275                 this.el.setLocation(-10000,-10000);
39276                 this.el.hide();
39277                 this.fireEvent("collapsed", this);
39278             },
39279             scope: this,
39280             block: true
39281         });
39282     },
39283
39284     animateExpand : function(){
39285         this.beforeSlide();
39286         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39287         this.el.setStyle("z-index", 20000);
39288         this.collapsedEl.hide({
39289             duration:.1
39290         });
39291         this.el.slideIn(this.getSlideAnchor(), {
39292             callback : function(){
39293                 this.el.setStyle("z-index", "");
39294                 this.afterSlide();
39295                 if(this.split){
39296                     this.split.el.show();
39297                 }
39298                 this.fireEvent("invalidated", this);
39299                 this.fireEvent("expanded", this);
39300             },
39301             scope: this,
39302             block: true
39303         });
39304     },
39305
39306     anchors : {
39307         "west" : "left",
39308         "east" : "right",
39309         "north" : "top",
39310         "south" : "bottom"
39311     },
39312
39313     sanchors : {
39314         "west" : "l",
39315         "east" : "r",
39316         "north" : "t",
39317         "south" : "b"
39318     },
39319
39320     canchors : {
39321         "west" : "tl-tr",
39322         "east" : "tr-tl",
39323         "north" : "tl-bl",
39324         "south" : "bl-tl"
39325     },
39326
39327     getAnchor : function(){
39328         return this.anchors[this.position];
39329     },
39330
39331     getCollapseAnchor : function(){
39332         return this.canchors[this.position];
39333     },
39334
39335     getSlideAnchor : function(){
39336         return this.sanchors[this.position];
39337     },
39338
39339     getAlignAdj : function(){
39340         var cm = this.cmargins;
39341         switch(this.position){
39342             case "west":
39343                 return [0, 0];
39344             break;
39345             case "east":
39346                 return [0, 0];
39347             break;
39348             case "north":
39349                 return [0, 0];
39350             break;
39351             case "south":
39352                 return [0, 0];
39353             break;
39354         }
39355     },
39356
39357     getExpandAdj : function(){
39358         var c = this.collapsedEl, cm = this.cmargins;
39359         switch(this.position){
39360             case "west":
39361                 return [-(cm.right+c.getWidth()+cm.left), 0];
39362             break;
39363             case "east":
39364                 return [cm.right+c.getWidth()+cm.left, 0];
39365             break;
39366             case "north":
39367                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39368             break;
39369             case "south":
39370                 return [0, cm.top+cm.bottom+c.getHeight()];
39371             break;
39372         }
39373     }
39374 });/*
39375  * Based on:
39376  * Ext JS Library 1.1.1
39377  * Copyright(c) 2006-2007, Ext JS, LLC.
39378  *
39379  * Originally Released Under LGPL - original licence link has changed is not relivant.
39380  *
39381  * Fork - LGPL
39382  * <script type="text/javascript">
39383  */
39384 /*
39385  * These classes are private internal classes
39386  */
39387 Roo.bootstrap.layout.Center = function(config){
39388     config.region = "center";
39389     Roo.bootstrap.layout.Region.call(this, config);
39390     this.visible = true;
39391     this.minWidth = config.minWidth || 20;
39392     this.minHeight = config.minHeight || 20;
39393 };
39394
39395 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39396     hide : function(){
39397         // center panel can't be hidden
39398     },
39399     
39400     show : function(){
39401         // center panel can't be hidden
39402     },
39403     
39404     getMinWidth: function(){
39405         return this.minWidth;
39406     },
39407     
39408     getMinHeight: function(){
39409         return this.minHeight;
39410     }
39411 });
39412
39413
39414
39415
39416  
39417
39418
39419
39420
39421
39422
39423 Roo.bootstrap.layout.North = function(config)
39424 {
39425     config.region = 'north';
39426     config.cursor = 'n-resize';
39427     
39428     Roo.bootstrap.layout.Split.call(this, config);
39429     
39430     
39431     if(this.split){
39432         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39433         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39434         this.split.el.addClass("roo-layout-split-v");
39435     }
39436     //var size = config.initialSize || config.height;
39437     //if(this.el && typeof size != "undefined"){
39438     //    this.el.setHeight(size);
39439     //}
39440 };
39441 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39442 {
39443     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39444      
39445      
39446     onRender : function(ctr, pos)
39447     {
39448         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39449         var size = this.config.initialSize || this.config.height;
39450         if(this.el && typeof size != "undefined"){
39451             this.el.setHeight(size);
39452         }
39453     
39454     },
39455     
39456     getBox : function(){
39457         if(this.collapsed){
39458             return this.collapsedEl.getBox();
39459         }
39460         var box = this.el.getBox();
39461         if(this.split){
39462             box.height += this.split.el.getHeight();
39463         }
39464         return box;
39465     },
39466     
39467     updateBox : function(box){
39468         if(this.split && !this.collapsed){
39469             box.height -= this.split.el.getHeight();
39470             this.split.el.setLeft(box.x);
39471             this.split.el.setTop(box.y+box.height);
39472             this.split.el.setWidth(box.width);
39473         }
39474         if(this.collapsed){
39475             this.updateBody(box.width, null);
39476         }
39477         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39478     }
39479 });
39480
39481
39482
39483
39484
39485 Roo.bootstrap.layout.South = function(config){
39486     config.region = 'south';
39487     config.cursor = 's-resize';
39488     Roo.bootstrap.layout.Split.call(this, config);
39489     if(this.split){
39490         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39491         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39492         this.split.el.addClass("roo-layout-split-v");
39493     }
39494     
39495 };
39496
39497 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39498     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39499     
39500     onRender : function(ctr, pos)
39501     {
39502         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39503         var size = this.config.initialSize || this.config.height;
39504         if(this.el && typeof size != "undefined"){
39505             this.el.setHeight(size);
39506         }
39507     
39508     },
39509     
39510     getBox : function(){
39511         if(this.collapsed){
39512             return this.collapsedEl.getBox();
39513         }
39514         var box = this.el.getBox();
39515         if(this.split){
39516             var sh = this.split.el.getHeight();
39517             box.height += sh;
39518             box.y -= sh;
39519         }
39520         return box;
39521     },
39522     
39523     updateBox : function(box){
39524         if(this.split && !this.collapsed){
39525             var sh = this.split.el.getHeight();
39526             box.height -= sh;
39527             box.y += sh;
39528             this.split.el.setLeft(box.x);
39529             this.split.el.setTop(box.y-sh);
39530             this.split.el.setWidth(box.width);
39531         }
39532         if(this.collapsed){
39533             this.updateBody(box.width, null);
39534         }
39535         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39536     }
39537 });
39538
39539 Roo.bootstrap.layout.East = function(config){
39540     config.region = "east";
39541     config.cursor = "e-resize";
39542     Roo.bootstrap.layout.Split.call(this, config);
39543     if(this.split){
39544         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39545         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39546         this.split.el.addClass("roo-layout-split-h");
39547     }
39548     
39549 };
39550 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39551     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39552     
39553     onRender : function(ctr, pos)
39554     {
39555         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39556         var size = this.config.initialSize || this.config.width;
39557         if(this.el && typeof size != "undefined"){
39558             this.el.setWidth(size);
39559         }
39560     
39561     },
39562     
39563     getBox : function(){
39564         if(this.collapsed){
39565             return this.collapsedEl.getBox();
39566         }
39567         var box = this.el.getBox();
39568         if(this.split){
39569             var sw = this.split.el.getWidth();
39570             box.width += sw;
39571             box.x -= sw;
39572         }
39573         return box;
39574     },
39575
39576     updateBox : function(box){
39577         if(this.split && !this.collapsed){
39578             var sw = this.split.el.getWidth();
39579             box.width -= sw;
39580             this.split.el.setLeft(box.x);
39581             this.split.el.setTop(box.y);
39582             this.split.el.setHeight(box.height);
39583             box.x += sw;
39584         }
39585         if(this.collapsed){
39586             this.updateBody(null, box.height);
39587         }
39588         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39589     }
39590 });
39591
39592 Roo.bootstrap.layout.West = function(config){
39593     config.region = "west";
39594     config.cursor = "w-resize";
39595     
39596     Roo.bootstrap.layout.Split.call(this, config);
39597     if(this.split){
39598         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39599         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39600         this.split.el.addClass("roo-layout-split-h");
39601     }
39602     
39603 };
39604 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39605     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39606     
39607     onRender: function(ctr, pos)
39608     {
39609         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39610         var size = this.config.initialSize || this.config.width;
39611         if(typeof size != "undefined"){
39612             this.el.setWidth(size);
39613         }
39614     },
39615     
39616     getBox : function(){
39617         if(this.collapsed){
39618             return this.collapsedEl.getBox();
39619         }
39620         var box = this.el.getBox();
39621         if (box.width == 0) {
39622             box.width = this.config.width; // kludge?
39623         }
39624         if(this.split){
39625             box.width += this.split.el.getWidth();
39626         }
39627         return box;
39628     },
39629     
39630     updateBox : function(box){
39631         if(this.split && !this.collapsed){
39632             var sw = this.split.el.getWidth();
39633             box.width -= sw;
39634             this.split.el.setLeft(box.x+box.width);
39635             this.split.el.setTop(box.y);
39636             this.split.el.setHeight(box.height);
39637         }
39638         if(this.collapsed){
39639             this.updateBody(null, box.height);
39640         }
39641         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39642     }
39643 });Roo.namespace("Roo.bootstrap.panel");/*
39644  * Based on:
39645  * Ext JS Library 1.1.1
39646  * Copyright(c) 2006-2007, Ext JS, LLC.
39647  *
39648  * Originally Released Under LGPL - original licence link has changed is not relivant.
39649  *
39650  * Fork - LGPL
39651  * <script type="text/javascript">
39652  */
39653 /**
39654  * @class Roo.ContentPanel
39655  * @extends Roo.util.Observable
39656  * A basic ContentPanel element.
39657  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39658  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39659  * @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
39660  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39661  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39662  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39663  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39664  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39665  * @cfg {String} title          The title for this panel
39666  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39667  * @cfg {String} url            Calls {@link #setUrl} with this value
39668  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39669  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39670  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39671  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39672  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39673  * @cfg {Boolean} badges render the badges
39674  * @cfg {String} cls  extra classes to use  
39675  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39676
39677  * @constructor
39678  * Create a new ContentPanel.
39679  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39680  * @param {String/Object} config A string to set only the title or a config object
39681  * @param {String} content (optional) Set the HTML content for this panel
39682  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39683  */
39684 Roo.bootstrap.panel.Content = function( config){
39685     
39686     this.tpl = config.tpl || false;
39687     
39688     var el = config.el;
39689     var content = config.content;
39690
39691     if(config.autoCreate){ // xtype is available if this is called from factory
39692         el = Roo.id();
39693     }
39694     this.el = Roo.get(el);
39695     if(!this.el && config && config.autoCreate){
39696         if(typeof config.autoCreate == "object"){
39697             if(!config.autoCreate.id){
39698                 config.autoCreate.id = config.id||el;
39699             }
39700             this.el = Roo.DomHelper.append(document.body,
39701                         config.autoCreate, true);
39702         }else{
39703             var elcfg =  {
39704                 tag: "div",
39705                 cls: (config.cls || '') +
39706                     (config.background ? ' bg-' + config.background : '') +
39707                     " roo-layout-inactive-content",
39708                 id: config.id||el
39709             };
39710             if (config.iframe) {
39711                 elcfg.cn = [
39712                     {
39713                         tag : 'iframe',
39714                         style : 'border: 0px',
39715                         src : 'about:blank'
39716                     }
39717                 ];
39718             }
39719               
39720             if (config.html) {
39721                 elcfg.html = config.html;
39722                 
39723             }
39724                         
39725             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39726             if (config.iframe) {
39727                 this.iframeEl = this.el.select('iframe',true).first();
39728             }
39729             
39730         }
39731     } 
39732     this.closable = false;
39733     this.loaded = false;
39734     this.active = false;
39735    
39736       
39737     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39738         
39739         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39740         
39741         this.wrapEl = this.el; //this.el.wrap();
39742         var ti = [];
39743         if (config.toolbar.items) {
39744             ti = config.toolbar.items ;
39745             delete config.toolbar.items ;
39746         }
39747         
39748         var nitems = [];
39749         this.toolbar.render(this.wrapEl, 'before');
39750         for(var i =0;i < ti.length;i++) {
39751           //  Roo.log(['add child', items[i]]);
39752             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39753         }
39754         this.toolbar.items = nitems;
39755         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39756         delete config.toolbar;
39757         
39758     }
39759     /*
39760     // xtype created footer. - not sure if will work as we normally have to render first..
39761     if (this.footer && !this.footer.el && this.footer.xtype) {
39762         if (!this.wrapEl) {
39763             this.wrapEl = this.el.wrap();
39764         }
39765     
39766         this.footer.container = this.wrapEl.createChild();
39767          
39768         this.footer = Roo.factory(this.footer, Roo);
39769         
39770     }
39771     */
39772     
39773      if(typeof config == "string"){
39774         this.title = config;
39775     }else{
39776         Roo.apply(this, config);
39777     }
39778     
39779     if(this.resizeEl){
39780         this.resizeEl = Roo.get(this.resizeEl, true);
39781     }else{
39782         this.resizeEl = this.el;
39783     }
39784     // handle view.xtype
39785     
39786  
39787     
39788     
39789     this.addEvents({
39790         /**
39791          * @event activate
39792          * Fires when this panel is activated. 
39793          * @param {Roo.ContentPanel} this
39794          */
39795         "activate" : true,
39796         /**
39797          * @event deactivate
39798          * Fires when this panel is activated. 
39799          * @param {Roo.ContentPanel} this
39800          */
39801         "deactivate" : true,
39802
39803         /**
39804          * @event resize
39805          * Fires when this panel is resized if fitToFrame is true.
39806          * @param {Roo.ContentPanel} this
39807          * @param {Number} width The width after any component adjustments
39808          * @param {Number} height The height after any component adjustments
39809          */
39810         "resize" : true,
39811         
39812          /**
39813          * @event render
39814          * Fires when this tab is created
39815          * @param {Roo.ContentPanel} this
39816          */
39817         "render" : true
39818         
39819         
39820         
39821     });
39822     
39823
39824     
39825     
39826     if(this.autoScroll && !this.iframe){
39827         this.resizeEl.setStyle("overflow", "auto");
39828     } else {
39829         // fix randome scrolling
39830         //this.el.on('scroll', function() {
39831         //    Roo.log('fix random scolling');
39832         //    this.scrollTo('top',0); 
39833         //});
39834     }
39835     content = content || this.content;
39836     if(content){
39837         this.setContent(content);
39838     }
39839     if(config && config.url){
39840         this.setUrl(this.url, this.params, this.loadOnce);
39841     }
39842     
39843     
39844     
39845     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39846     
39847     if (this.view && typeof(this.view.xtype) != 'undefined') {
39848         this.view.el = this.el.appendChild(document.createElement("div"));
39849         this.view = Roo.factory(this.view); 
39850         this.view.render  &&  this.view.render(false, '');  
39851     }
39852     
39853     
39854     this.fireEvent('render', this);
39855 };
39856
39857 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39858     
39859     cls : '',
39860     background : '',
39861     
39862     tabTip : '',
39863     
39864     iframe : false,
39865     iframeEl : false,
39866     
39867     setRegion : function(region){
39868         this.region = region;
39869         this.setActiveClass(region && !this.background);
39870     },
39871     
39872     
39873     setActiveClass: function(state)
39874     {
39875         if(state){
39876            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39877            this.el.setStyle('position','relative');
39878         }else{
39879            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39880            this.el.setStyle('position', 'absolute');
39881         } 
39882     },
39883     
39884     /**
39885      * Returns the toolbar for this Panel if one was configured. 
39886      * @return {Roo.Toolbar} 
39887      */
39888     getToolbar : function(){
39889         return this.toolbar;
39890     },
39891     
39892     setActiveState : function(active)
39893     {
39894         this.active = active;
39895         this.setActiveClass(active);
39896         if(!active){
39897             if(this.fireEvent("deactivate", this) === false){
39898                 return false;
39899             }
39900             return true;
39901         }
39902         this.fireEvent("activate", this);
39903         return true;
39904     },
39905     /**
39906      * Updates this panel's element (not for iframe)
39907      * @param {String} content The new content
39908      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39909     */
39910     setContent : function(content, loadScripts){
39911         if (this.iframe) {
39912             return;
39913         }
39914         
39915         this.el.update(content, loadScripts);
39916     },
39917
39918     ignoreResize : function(w, h){
39919         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39920             return true;
39921         }else{
39922             this.lastSize = {width: w, height: h};
39923             return false;
39924         }
39925     },
39926     /**
39927      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39928      * @return {Roo.UpdateManager} The UpdateManager
39929      */
39930     getUpdateManager : function(){
39931         if (this.iframe) {
39932             return false;
39933         }
39934         return this.el.getUpdateManager();
39935     },
39936      /**
39937      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39938      * Does not work with IFRAME contents
39939      * @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:
39940 <pre><code>
39941 panel.load({
39942     url: "your-url.php",
39943     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39944     callback: yourFunction,
39945     scope: yourObject, //(optional scope)
39946     discardUrl: false,
39947     nocache: false,
39948     text: "Loading...",
39949     timeout: 30,
39950     scripts: false
39951 });
39952 </code></pre>
39953      
39954      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39955      * 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.
39956      * @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}
39957      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39958      * @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.
39959      * @return {Roo.ContentPanel} this
39960      */
39961     load : function(){
39962         
39963         if (this.iframe) {
39964             return this;
39965         }
39966         
39967         var um = this.el.getUpdateManager();
39968         um.update.apply(um, arguments);
39969         return this;
39970     },
39971
39972
39973     /**
39974      * 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.
39975      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39976      * @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)
39977      * @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)
39978      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39979      */
39980     setUrl : function(url, params, loadOnce){
39981         if (this.iframe) {
39982             this.iframeEl.dom.src = url;
39983             return false;
39984         }
39985         
39986         if(this.refreshDelegate){
39987             this.removeListener("activate", this.refreshDelegate);
39988         }
39989         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39990         this.on("activate", this.refreshDelegate);
39991         return this.el.getUpdateManager();
39992     },
39993     
39994     _handleRefresh : function(url, params, loadOnce){
39995         if(!loadOnce || !this.loaded){
39996             var updater = this.el.getUpdateManager();
39997             updater.update(url, params, this._setLoaded.createDelegate(this));
39998         }
39999     },
40000     
40001     _setLoaded : function(){
40002         this.loaded = true;
40003     }, 
40004     
40005     /**
40006      * Returns this panel's id
40007      * @return {String} 
40008      */
40009     getId : function(){
40010         return this.el.id;
40011     },
40012     
40013     /** 
40014      * Returns this panel's element - used by regiosn to add.
40015      * @return {Roo.Element} 
40016      */
40017     getEl : function(){
40018         return this.wrapEl || this.el;
40019     },
40020     
40021    
40022     
40023     adjustForComponents : function(width, height)
40024     {
40025         //Roo.log('adjustForComponents ');
40026         if(this.resizeEl != this.el){
40027             width -= this.el.getFrameWidth('lr');
40028             height -= this.el.getFrameWidth('tb');
40029         }
40030         if(this.toolbar){
40031             var te = this.toolbar.getEl();
40032             te.setWidth(width);
40033             height -= te.getHeight();
40034         }
40035         if(this.footer){
40036             var te = this.footer.getEl();
40037             te.setWidth(width);
40038             height -= te.getHeight();
40039         }
40040         
40041         
40042         if(this.adjustments){
40043             width += this.adjustments[0];
40044             height += this.adjustments[1];
40045         }
40046         return {"width": width, "height": height};
40047     },
40048     
40049     setSize : function(width, height){
40050         if(this.fitToFrame && !this.ignoreResize(width, height)){
40051             if(this.fitContainer && this.resizeEl != this.el){
40052                 this.el.setSize(width, height);
40053             }
40054             var size = this.adjustForComponents(width, height);
40055             if (this.iframe) {
40056                 this.iframeEl.setSize(width,height);
40057             }
40058             
40059             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40060             this.fireEvent('resize', this, size.width, size.height);
40061             
40062             
40063         }
40064     },
40065     
40066     /**
40067      * Returns this panel's title
40068      * @return {String} 
40069      */
40070     getTitle : function(){
40071         
40072         if (typeof(this.title) != 'object') {
40073             return this.title;
40074         }
40075         
40076         var t = '';
40077         for (var k in this.title) {
40078             if (!this.title.hasOwnProperty(k)) {
40079                 continue;
40080             }
40081             
40082             if (k.indexOf('-') >= 0) {
40083                 var s = k.split('-');
40084                 for (var i = 0; i<s.length; i++) {
40085                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40086                 }
40087             } else {
40088                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40089             }
40090         }
40091         return t;
40092     },
40093     
40094     /**
40095      * Set this panel's title
40096      * @param {String} title
40097      */
40098     setTitle : function(title){
40099         this.title = title;
40100         if(this.region){
40101             this.region.updatePanelTitle(this, title);
40102         }
40103     },
40104     
40105     /**
40106      * Returns true is this panel was configured to be closable
40107      * @return {Boolean} 
40108      */
40109     isClosable : function(){
40110         return this.closable;
40111     },
40112     
40113     beforeSlide : function(){
40114         this.el.clip();
40115         this.resizeEl.clip();
40116     },
40117     
40118     afterSlide : function(){
40119         this.el.unclip();
40120         this.resizeEl.unclip();
40121     },
40122     
40123     /**
40124      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40125      *   Will fail silently if the {@link #setUrl} method has not been called.
40126      *   This does not activate the panel, just updates its content.
40127      */
40128     refresh : function(){
40129         if(this.refreshDelegate){
40130            this.loaded = false;
40131            this.refreshDelegate();
40132         }
40133     },
40134     
40135     /**
40136      * Destroys this panel
40137      */
40138     destroy : function(){
40139         this.el.removeAllListeners();
40140         var tempEl = document.createElement("span");
40141         tempEl.appendChild(this.el.dom);
40142         tempEl.innerHTML = "";
40143         this.el.remove();
40144         this.el = null;
40145     },
40146     
40147     /**
40148      * form - if the content panel contains a form - this is a reference to it.
40149      * @type {Roo.form.Form}
40150      */
40151     form : false,
40152     /**
40153      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40154      *    This contains a reference to it.
40155      * @type {Roo.View}
40156      */
40157     view : false,
40158     
40159       /**
40160      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40161      * <pre><code>
40162
40163 layout.addxtype({
40164        xtype : 'Form',
40165        items: [ .... ]
40166    }
40167 );
40168
40169 </code></pre>
40170      * @param {Object} cfg Xtype definition of item to add.
40171      */
40172     
40173     
40174     getChildContainer: function () {
40175         return this.getEl();
40176     }
40177     
40178     
40179     /*
40180         var  ret = new Roo.factory(cfg);
40181         return ret;
40182         
40183         
40184         // add form..
40185         if (cfg.xtype.match(/^Form$/)) {
40186             
40187             var el;
40188             //if (this.footer) {
40189             //    el = this.footer.container.insertSibling(false, 'before');
40190             //} else {
40191                 el = this.el.createChild();
40192             //}
40193
40194             this.form = new  Roo.form.Form(cfg);
40195             
40196             
40197             if ( this.form.allItems.length) {
40198                 this.form.render(el.dom);
40199             }
40200             return this.form;
40201         }
40202         // should only have one of theses..
40203         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40204             // views.. should not be just added - used named prop 'view''
40205             
40206             cfg.el = this.el.appendChild(document.createElement("div"));
40207             // factory?
40208             
40209             var ret = new Roo.factory(cfg);
40210              
40211              ret.render && ret.render(false, ''); // render blank..
40212             this.view = ret;
40213             return ret;
40214         }
40215         return false;
40216     }
40217     \*/
40218 });
40219  
40220 /**
40221  * @class Roo.bootstrap.panel.Grid
40222  * @extends Roo.bootstrap.panel.Content
40223  * @constructor
40224  * Create a new GridPanel.
40225  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40226  * @param {Object} config A the config object
40227   
40228  */
40229
40230
40231
40232 Roo.bootstrap.panel.Grid = function(config)
40233 {
40234     
40235       
40236     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40237         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40238
40239     config.el = this.wrapper;
40240     //this.el = this.wrapper;
40241     
40242       if (config.container) {
40243         // ctor'ed from a Border/panel.grid
40244         
40245         
40246         this.wrapper.setStyle("overflow", "hidden");
40247         this.wrapper.addClass('roo-grid-container');
40248
40249     }
40250     
40251     
40252     if(config.toolbar){
40253         var tool_el = this.wrapper.createChild();    
40254         this.toolbar = Roo.factory(config.toolbar);
40255         var ti = [];
40256         if (config.toolbar.items) {
40257             ti = config.toolbar.items ;
40258             delete config.toolbar.items ;
40259         }
40260         
40261         var nitems = [];
40262         this.toolbar.render(tool_el);
40263         for(var i =0;i < ti.length;i++) {
40264           //  Roo.log(['add child', items[i]]);
40265             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40266         }
40267         this.toolbar.items = nitems;
40268         
40269         delete config.toolbar;
40270     }
40271     
40272     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40273     config.grid.scrollBody = true;;
40274     config.grid.monitorWindowResize = false; // turn off autosizing
40275     config.grid.autoHeight = false;
40276     config.grid.autoWidth = false;
40277     
40278     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40279     
40280     if (config.background) {
40281         // render grid on panel activation (if panel background)
40282         this.on('activate', function(gp) {
40283             if (!gp.grid.rendered) {
40284                 gp.grid.render(this.wrapper);
40285                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40286             }
40287         });
40288             
40289     } else {
40290         this.grid.render(this.wrapper);
40291         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40292
40293     }
40294     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40295     // ??? needed ??? config.el = this.wrapper;
40296     
40297     
40298     
40299   
40300     // xtype created footer. - not sure if will work as we normally have to render first..
40301     if (this.footer && !this.footer.el && this.footer.xtype) {
40302         
40303         var ctr = this.grid.getView().getFooterPanel(true);
40304         this.footer.dataSource = this.grid.dataSource;
40305         this.footer = Roo.factory(this.footer, Roo);
40306         this.footer.render(ctr);
40307         
40308     }
40309     
40310     
40311     
40312     
40313      
40314 };
40315
40316 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40317     getId : function(){
40318         return this.grid.id;
40319     },
40320     
40321     /**
40322      * Returns the grid for this panel
40323      * @return {Roo.bootstrap.Table} 
40324      */
40325     getGrid : function(){
40326         return this.grid;    
40327     },
40328     
40329     setSize : function(width, height){
40330         if(!this.ignoreResize(width, height)){
40331             var grid = this.grid;
40332             var size = this.adjustForComponents(width, height);
40333             // tfoot is not a footer?
40334           
40335             
40336             var gridel = grid.getGridEl();
40337             gridel.setSize(size.width, size.height);
40338             
40339             var tbd = grid.getGridEl().select('tbody', true).first();
40340             var thd = grid.getGridEl().select('thead',true).first();
40341             var tbf= grid.getGridEl().select('tfoot', true).first();
40342
40343             if (tbf) {
40344                 size.height -= tbf.getHeight();
40345             }
40346             if (thd) {
40347                 size.height -= thd.getHeight();
40348             }
40349             
40350             tbd.setSize(size.width, size.height );
40351             // this is for the account management tab -seems to work there.
40352             var thd = grid.getGridEl().select('thead',true).first();
40353             //if (tbd) {
40354             //    tbd.setSize(size.width, size.height - thd.getHeight());
40355             //}
40356              
40357             grid.autoSize();
40358         }
40359     },
40360      
40361     
40362     
40363     beforeSlide : function(){
40364         this.grid.getView().scroller.clip();
40365     },
40366     
40367     afterSlide : function(){
40368         this.grid.getView().scroller.unclip();
40369     },
40370     
40371     destroy : function(){
40372         this.grid.destroy();
40373         delete this.grid;
40374         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40375     }
40376 });
40377
40378 /**
40379  * @class Roo.bootstrap.panel.Nest
40380  * @extends Roo.bootstrap.panel.Content
40381  * @constructor
40382  * Create a new Panel, that can contain a layout.Border.
40383  * 
40384  * 
40385  * @param {Roo.BorderLayout} layout The layout for this panel
40386  * @param {String/Object} config A string to set only the title or a config object
40387  */
40388 Roo.bootstrap.panel.Nest = function(config)
40389 {
40390     // construct with only one argument..
40391     /* FIXME - implement nicer consturctors
40392     if (layout.layout) {
40393         config = layout;
40394         layout = config.layout;
40395         delete config.layout;
40396     }
40397     if (layout.xtype && !layout.getEl) {
40398         // then layout needs constructing..
40399         layout = Roo.factory(layout, Roo);
40400     }
40401     */
40402     
40403     config.el =  config.layout.getEl();
40404     
40405     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40406     
40407     config.layout.monitorWindowResize = false; // turn off autosizing
40408     this.layout = config.layout;
40409     this.layout.getEl().addClass("roo-layout-nested-layout");
40410     this.layout.parent = this;
40411     
40412     
40413     
40414     
40415 };
40416
40417 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40418
40419     setSize : function(width, height){
40420         if(!this.ignoreResize(width, height)){
40421             var size = this.adjustForComponents(width, height);
40422             var el = this.layout.getEl();
40423             if (size.height < 1) {
40424                 el.setWidth(size.width);   
40425             } else {
40426                 el.setSize(size.width, size.height);
40427             }
40428             var touch = el.dom.offsetWidth;
40429             this.layout.layout();
40430             // ie requires a double layout on the first pass
40431             if(Roo.isIE && !this.initialized){
40432                 this.initialized = true;
40433                 this.layout.layout();
40434             }
40435         }
40436     },
40437     
40438     // activate all subpanels if not currently active..
40439     
40440     setActiveState : function(active){
40441         this.active = active;
40442         this.setActiveClass(active);
40443         
40444         if(!active){
40445             this.fireEvent("deactivate", this);
40446             return;
40447         }
40448         
40449         this.fireEvent("activate", this);
40450         // not sure if this should happen before or after..
40451         if (!this.layout) {
40452             return; // should not happen..
40453         }
40454         var reg = false;
40455         for (var r in this.layout.regions) {
40456             reg = this.layout.getRegion(r);
40457             if (reg.getActivePanel()) {
40458                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40459                 reg.setActivePanel(reg.getActivePanel());
40460                 continue;
40461             }
40462             if (!reg.panels.length) {
40463                 continue;
40464             }
40465             reg.showPanel(reg.getPanel(0));
40466         }
40467         
40468         
40469         
40470         
40471     },
40472     
40473     /**
40474      * Returns the nested BorderLayout for this panel
40475      * @return {Roo.BorderLayout} 
40476      */
40477     getLayout : function(){
40478         return this.layout;
40479     },
40480     
40481      /**
40482      * Adds a xtype elements to the layout of the nested panel
40483      * <pre><code>
40484
40485 panel.addxtype({
40486        xtype : 'ContentPanel',
40487        region: 'west',
40488        items: [ .... ]
40489    }
40490 );
40491
40492 panel.addxtype({
40493         xtype : 'NestedLayoutPanel',
40494         region: 'west',
40495         layout: {
40496            center: { },
40497            west: { }   
40498         },
40499         items : [ ... list of content panels or nested layout panels.. ]
40500    }
40501 );
40502 </code></pre>
40503      * @param {Object} cfg Xtype definition of item to add.
40504      */
40505     addxtype : function(cfg) {
40506         return this.layout.addxtype(cfg);
40507     
40508     }
40509 });/*
40510  * Based on:
40511  * Ext JS Library 1.1.1
40512  * Copyright(c) 2006-2007, Ext JS, LLC.
40513  *
40514  * Originally Released Under LGPL - original licence link has changed is not relivant.
40515  *
40516  * Fork - LGPL
40517  * <script type="text/javascript">
40518  */
40519 /**
40520  * @class Roo.TabPanel
40521  * @extends Roo.util.Observable
40522  * A lightweight tab container.
40523  * <br><br>
40524  * Usage:
40525  * <pre><code>
40526 // basic tabs 1, built from existing content
40527 var tabs = new Roo.TabPanel("tabs1");
40528 tabs.addTab("script", "View Script");
40529 tabs.addTab("markup", "View Markup");
40530 tabs.activate("script");
40531
40532 // more advanced tabs, built from javascript
40533 var jtabs = new Roo.TabPanel("jtabs");
40534 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40535
40536 // set up the UpdateManager
40537 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40538 var updater = tab2.getUpdateManager();
40539 updater.setDefaultUrl("ajax1.htm");
40540 tab2.on('activate', updater.refresh, updater, true);
40541
40542 // Use setUrl for Ajax loading
40543 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40544 tab3.setUrl("ajax2.htm", null, true);
40545
40546 // Disabled tab
40547 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40548 tab4.disable();
40549
40550 jtabs.activate("jtabs-1");
40551  * </code></pre>
40552  * @constructor
40553  * Create a new TabPanel.
40554  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40555  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40556  */
40557 Roo.bootstrap.panel.Tabs = function(config){
40558     /**
40559     * The container element for this TabPanel.
40560     * @type Roo.Element
40561     */
40562     this.el = Roo.get(config.el);
40563     delete config.el;
40564     if(config){
40565         if(typeof config == "boolean"){
40566             this.tabPosition = config ? "bottom" : "top";
40567         }else{
40568             Roo.apply(this, config);
40569         }
40570     }
40571     
40572     if(this.tabPosition == "bottom"){
40573         // if tabs are at the bottom = create the body first.
40574         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40575         this.el.addClass("roo-tabs-bottom");
40576     }
40577     // next create the tabs holders
40578     
40579     if (this.tabPosition == "west"){
40580         
40581         var reg = this.region; // fake it..
40582         while (reg) {
40583             if (!reg.mgr.parent) {
40584                 break;
40585             }
40586             reg = reg.mgr.parent.region;
40587         }
40588         Roo.log("got nest?");
40589         Roo.log(reg);
40590         if (reg.mgr.getRegion('west')) {
40591             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40592             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40593             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40594             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40595             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40596         
40597             
40598         }
40599         
40600         
40601     } else {
40602      
40603         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40604         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40605         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40606         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40607     }
40608     
40609     
40610     if(Roo.isIE){
40611         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40612     }
40613     
40614     // finally - if tabs are at the top, then create the body last..
40615     if(this.tabPosition != "bottom"){
40616         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40617          * @type Roo.Element
40618          */
40619         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40620         this.el.addClass("roo-tabs-top");
40621     }
40622     this.items = [];
40623
40624     this.bodyEl.setStyle("position", "relative");
40625
40626     this.active = null;
40627     this.activateDelegate = this.activate.createDelegate(this);
40628
40629     this.addEvents({
40630         /**
40631          * @event tabchange
40632          * Fires when the active tab changes
40633          * @param {Roo.TabPanel} this
40634          * @param {Roo.TabPanelItem} activePanel The new active tab
40635          */
40636         "tabchange": true,
40637         /**
40638          * @event beforetabchange
40639          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40640          * @param {Roo.TabPanel} this
40641          * @param {Object} e Set cancel to true on this object to cancel the tab change
40642          * @param {Roo.TabPanelItem} tab The tab being changed to
40643          */
40644         "beforetabchange" : true
40645     });
40646
40647     Roo.EventManager.onWindowResize(this.onResize, this);
40648     this.cpad = this.el.getPadding("lr");
40649     this.hiddenCount = 0;
40650
40651
40652     // toolbar on the tabbar support...
40653     if (this.toolbar) {
40654         alert("no toolbar support yet");
40655         this.toolbar  = false;
40656         /*
40657         var tcfg = this.toolbar;
40658         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40659         this.toolbar = new Roo.Toolbar(tcfg);
40660         if (Roo.isSafari) {
40661             var tbl = tcfg.container.child('table', true);
40662             tbl.setAttribute('width', '100%');
40663         }
40664         */
40665         
40666     }
40667    
40668
40669
40670     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40671 };
40672
40673 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40674     /*
40675      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40676      */
40677     tabPosition : "top",
40678     /*
40679      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40680      */
40681     currentTabWidth : 0,
40682     /*
40683      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40684      */
40685     minTabWidth : 40,
40686     /*
40687      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40688      */
40689     maxTabWidth : 250,
40690     /*
40691      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40692      */
40693     preferredTabWidth : 175,
40694     /*
40695      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40696      */
40697     resizeTabs : false,
40698     /*
40699      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40700      */
40701     monitorResize : true,
40702     /*
40703      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40704      */
40705     toolbar : false,  // set by caller..
40706     
40707     region : false, /// set by caller
40708     
40709     disableTooltips : true, // not used yet...
40710
40711     /**
40712      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40713      * @param {String} id The id of the div to use <b>or create</b>
40714      * @param {String} text The text for the tab
40715      * @param {String} content (optional) Content to put in the TabPanelItem body
40716      * @param {Boolean} closable (optional) True to create a close icon on the tab
40717      * @return {Roo.TabPanelItem} The created TabPanelItem
40718      */
40719     addTab : function(id, text, content, closable, tpl)
40720     {
40721         var item = new Roo.bootstrap.panel.TabItem({
40722             panel: this,
40723             id : id,
40724             text : text,
40725             closable : closable,
40726             tpl : tpl
40727         });
40728         this.addTabItem(item);
40729         if(content){
40730             item.setContent(content);
40731         }
40732         return item;
40733     },
40734
40735     /**
40736      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40737      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40738      * @return {Roo.TabPanelItem}
40739      */
40740     getTab : function(id){
40741         return this.items[id];
40742     },
40743
40744     /**
40745      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40746      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40747      */
40748     hideTab : function(id){
40749         var t = this.items[id];
40750         if(!t.isHidden()){
40751            t.setHidden(true);
40752            this.hiddenCount++;
40753            this.autoSizeTabs();
40754         }
40755     },
40756
40757     /**
40758      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40759      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40760      */
40761     unhideTab : function(id){
40762         var t = this.items[id];
40763         if(t.isHidden()){
40764            t.setHidden(false);
40765            this.hiddenCount--;
40766            this.autoSizeTabs();
40767         }
40768     },
40769
40770     /**
40771      * Adds an existing {@link Roo.TabPanelItem}.
40772      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40773      */
40774     addTabItem : function(item)
40775     {
40776         this.items[item.id] = item;
40777         this.items.push(item);
40778         this.autoSizeTabs();
40779       //  if(this.resizeTabs){
40780     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40781   //         this.autoSizeTabs();
40782 //        }else{
40783 //            item.autoSize();
40784        // }
40785     },
40786
40787     /**
40788      * Removes a {@link Roo.TabPanelItem}.
40789      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40790      */
40791     removeTab : function(id){
40792         var items = this.items;
40793         var tab = items[id];
40794         if(!tab) { return; }
40795         var index = items.indexOf(tab);
40796         if(this.active == tab && items.length > 1){
40797             var newTab = this.getNextAvailable(index);
40798             if(newTab) {
40799                 newTab.activate();
40800             }
40801         }
40802         this.stripEl.dom.removeChild(tab.pnode.dom);
40803         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40804             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40805         }
40806         items.splice(index, 1);
40807         delete this.items[tab.id];
40808         tab.fireEvent("close", tab);
40809         tab.purgeListeners();
40810         this.autoSizeTabs();
40811     },
40812
40813     getNextAvailable : function(start){
40814         var items = this.items;
40815         var index = start;
40816         // look for a next tab that will slide over to
40817         // replace the one being removed
40818         while(index < items.length){
40819             var item = items[++index];
40820             if(item && !item.isHidden()){
40821                 return item;
40822             }
40823         }
40824         // if one isn't found select the previous tab (on the left)
40825         index = start;
40826         while(index >= 0){
40827             var item = items[--index];
40828             if(item && !item.isHidden()){
40829                 return item;
40830             }
40831         }
40832         return null;
40833     },
40834
40835     /**
40836      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40837      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40838      */
40839     disableTab : function(id){
40840         var tab = this.items[id];
40841         if(tab && this.active != tab){
40842             tab.disable();
40843         }
40844     },
40845
40846     /**
40847      * Enables a {@link Roo.TabPanelItem} that is disabled.
40848      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40849      */
40850     enableTab : function(id){
40851         var tab = this.items[id];
40852         tab.enable();
40853     },
40854
40855     /**
40856      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40857      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40858      * @return {Roo.TabPanelItem} The TabPanelItem.
40859      */
40860     activate : function(id)
40861     {
40862         //Roo.log('activite:'  + id);
40863         
40864         var tab = this.items[id];
40865         if(!tab){
40866             return null;
40867         }
40868         if(tab == this.active || tab.disabled){
40869             return tab;
40870         }
40871         var e = {};
40872         this.fireEvent("beforetabchange", this, e, tab);
40873         if(e.cancel !== true && !tab.disabled){
40874             if(this.active){
40875                 this.active.hide();
40876             }
40877             this.active = this.items[id];
40878             this.active.show();
40879             this.fireEvent("tabchange", this, this.active);
40880         }
40881         return tab;
40882     },
40883
40884     /**
40885      * Gets the active {@link Roo.TabPanelItem}.
40886      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40887      */
40888     getActiveTab : function(){
40889         return this.active;
40890     },
40891
40892     /**
40893      * Updates the tab body element to fit the height of the container element
40894      * for overflow scrolling
40895      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40896      */
40897     syncHeight : function(targetHeight){
40898         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40899         var bm = this.bodyEl.getMargins();
40900         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40901         this.bodyEl.setHeight(newHeight);
40902         return newHeight;
40903     },
40904
40905     onResize : function(){
40906         if(this.monitorResize){
40907             this.autoSizeTabs();
40908         }
40909     },
40910
40911     /**
40912      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40913      */
40914     beginUpdate : function(){
40915         this.updating = true;
40916     },
40917
40918     /**
40919      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40920      */
40921     endUpdate : function(){
40922         this.updating = false;
40923         this.autoSizeTabs();
40924     },
40925
40926     /**
40927      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40928      */
40929     autoSizeTabs : function()
40930     {
40931         var count = this.items.length;
40932         var vcount = count - this.hiddenCount;
40933         
40934         if (vcount < 2) {
40935             this.stripEl.hide();
40936         } else {
40937             this.stripEl.show();
40938         }
40939         
40940         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40941             return;
40942         }
40943         
40944         
40945         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40946         var availWidth = Math.floor(w / vcount);
40947         var b = this.stripBody;
40948         if(b.getWidth() > w){
40949             var tabs = this.items;
40950             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40951             if(availWidth < this.minTabWidth){
40952                 /*if(!this.sleft){    // incomplete scrolling code
40953                     this.createScrollButtons();
40954                 }
40955                 this.showScroll();
40956                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40957             }
40958         }else{
40959             if(this.currentTabWidth < this.preferredTabWidth){
40960                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40961             }
40962         }
40963     },
40964
40965     /**
40966      * Returns the number of tabs in this TabPanel.
40967      * @return {Number}
40968      */
40969      getCount : function(){
40970          return this.items.length;
40971      },
40972
40973     /**
40974      * Resizes all the tabs to the passed width
40975      * @param {Number} The new width
40976      */
40977     setTabWidth : function(width){
40978         this.currentTabWidth = width;
40979         for(var i = 0, len = this.items.length; i < len; i++) {
40980                 if(!this.items[i].isHidden()) {
40981                 this.items[i].setWidth(width);
40982             }
40983         }
40984     },
40985
40986     /**
40987      * Destroys this TabPanel
40988      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40989      */
40990     destroy : function(removeEl){
40991         Roo.EventManager.removeResizeListener(this.onResize, this);
40992         for(var i = 0, len = this.items.length; i < len; i++){
40993             this.items[i].purgeListeners();
40994         }
40995         if(removeEl === true){
40996             this.el.update("");
40997             this.el.remove();
40998         }
40999     },
41000     
41001     createStrip : function(container)
41002     {
41003         var strip = document.createElement("nav");
41004         strip.className = Roo.bootstrap.version == 4 ?
41005             "navbar-light bg-light" : 
41006             "navbar navbar-default"; //"x-tabs-wrap";
41007         container.appendChild(strip);
41008         return strip;
41009     },
41010     
41011     createStripList : function(strip)
41012     {
41013         // div wrapper for retard IE
41014         // returns the "tr" element.
41015         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
41016         //'<div class="x-tabs-strip-wrap">'+
41017           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
41018           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
41019         return strip.firstChild; //.firstChild.firstChild.firstChild;
41020     },
41021     createBody : function(container)
41022     {
41023         var body = document.createElement("div");
41024         Roo.id(body, "tab-body");
41025         //Roo.fly(body).addClass("x-tabs-body");
41026         Roo.fly(body).addClass("tab-content");
41027         container.appendChild(body);
41028         return body;
41029     },
41030     createItemBody :function(bodyEl, id){
41031         var body = Roo.getDom(id);
41032         if(!body){
41033             body = document.createElement("div");
41034             body.id = id;
41035         }
41036         //Roo.fly(body).addClass("x-tabs-item-body");
41037         Roo.fly(body).addClass("tab-pane");
41038          bodyEl.insertBefore(body, bodyEl.firstChild);
41039         return body;
41040     },
41041     /** @private */
41042     createStripElements :  function(stripEl, text, closable, tpl)
41043     {
41044         var td = document.createElement("li"); // was td..
41045         td.className = 'nav-item';
41046         
41047         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
41048         
41049         
41050         stripEl.appendChild(td);
41051         /*if(closable){
41052             td.className = "x-tabs-closable";
41053             if(!this.closeTpl){
41054                 this.closeTpl = new Roo.Template(
41055                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41056                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41057                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41058                 );
41059             }
41060             var el = this.closeTpl.overwrite(td, {"text": text});
41061             var close = el.getElementsByTagName("div")[0];
41062             var inner = el.getElementsByTagName("em")[0];
41063             return {"el": el, "close": close, "inner": inner};
41064         } else {
41065         */
41066         // not sure what this is..
41067 //            if(!this.tabTpl){
41068                 //this.tabTpl = new Roo.Template(
41069                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41070                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41071                 //);
41072 //                this.tabTpl = new Roo.Template(
41073 //                   '<a href="#">' +
41074 //                   '<span unselectable="on"' +
41075 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41076 //                            ' >{text}</span></a>'
41077 //                );
41078 //                
41079 //            }
41080
41081
41082             var template = tpl || this.tabTpl || false;
41083             
41084             if(!template){
41085                 template =  new Roo.Template(
41086                         Roo.bootstrap.version == 4 ? 
41087                             (
41088                                 '<a class="nav-link" href="#" unselectable="on"' +
41089                                      (this.disableTooltips ? '' : ' title="{text}"') +
41090                                      ' >{text}</a>'
41091                             ) : (
41092                                 '<a class="nav-link" href="#">' +
41093                                 '<span unselectable="on"' +
41094                                          (this.disableTooltips ? '' : ' title="{text}"') +
41095                                     ' >{text}</span></a>'
41096                             )
41097                 );
41098             }
41099             
41100             switch (typeof(template)) {
41101                 case 'object' :
41102                     break;
41103                 case 'string' :
41104                     template = new Roo.Template(template);
41105                     break;
41106                 default :
41107                     break;
41108             }
41109             
41110             var el = template.overwrite(td, {"text": text});
41111             
41112             var inner = el.getElementsByTagName("span")[0];
41113             
41114             return {"el": el, "inner": inner};
41115             
41116     }
41117         
41118     
41119 });
41120
41121 /**
41122  * @class Roo.TabPanelItem
41123  * @extends Roo.util.Observable
41124  * Represents an individual item (tab plus body) in a TabPanel.
41125  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41126  * @param {String} id The id of this TabPanelItem
41127  * @param {String} text The text for the tab of this TabPanelItem
41128  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41129  */
41130 Roo.bootstrap.panel.TabItem = function(config){
41131     /**
41132      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41133      * @type Roo.TabPanel
41134      */
41135     this.tabPanel = config.panel;
41136     /**
41137      * The id for this TabPanelItem
41138      * @type String
41139      */
41140     this.id = config.id;
41141     /** @private */
41142     this.disabled = false;
41143     /** @private */
41144     this.text = config.text;
41145     /** @private */
41146     this.loaded = false;
41147     this.closable = config.closable;
41148
41149     /**
41150      * The body element for this TabPanelItem.
41151      * @type Roo.Element
41152      */
41153     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41154     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41155     this.bodyEl.setStyle("display", "block");
41156     this.bodyEl.setStyle("zoom", "1");
41157     //this.hideAction();
41158
41159     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41160     /** @private */
41161     this.el = Roo.get(els.el);
41162     this.inner = Roo.get(els.inner, true);
41163      this.textEl = Roo.bootstrap.version == 4 ?
41164         this.el : Roo.get(this.el.dom.firstChild, true);
41165
41166     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41167     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41168
41169     
41170 //    this.el.on("mousedown", this.onTabMouseDown, this);
41171     this.el.on("click", this.onTabClick, this);
41172     /** @private */
41173     if(config.closable){
41174         var c = Roo.get(els.close, true);
41175         c.dom.title = this.closeText;
41176         c.addClassOnOver("close-over");
41177         c.on("click", this.closeClick, this);
41178      }
41179
41180     this.addEvents({
41181          /**
41182          * @event activate
41183          * Fires when this tab becomes the active tab.
41184          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41185          * @param {Roo.TabPanelItem} this
41186          */
41187         "activate": true,
41188         /**
41189          * @event beforeclose
41190          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41191          * @param {Roo.TabPanelItem} this
41192          * @param {Object} e Set cancel to true on this object to cancel the close.
41193          */
41194         "beforeclose": true,
41195         /**
41196          * @event close
41197          * Fires when this tab is closed.
41198          * @param {Roo.TabPanelItem} this
41199          */
41200          "close": true,
41201         /**
41202          * @event deactivate
41203          * Fires when this tab is no longer the active tab.
41204          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41205          * @param {Roo.TabPanelItem} this
41206          */
41207          "deactivate" : true
41208     });
41209     this.hidden = false;
41210
41211     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41212 };
41213
41214 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41215            {
41216     purgeListeners : function(){
41217        Roo.util.Observable.prototype.purgeListeners.call(this);
41218        this.el.removeAllListeners();
41219     },
41220     /**
41221      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41222      */
41223     show : function(){
41224         this.status_node.addClass("active");
41225         this.showAction();
41226         if(Roo.isOpera){
41227             this.tabPanel.stripWrap.repaint();
41228         }
41229         this.fireEvent("activate", this.tabPanel, this);
41230     },
41231
41232     /**
41233      * Returns true if this tab is the active tab.
41234      * @return {Boolean}
41235      */
41236     isActive : function(){
41237         return this.tabPanel.getActiveTab() == this;
41238     },
41239
41240     /**
41241      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41242      */
41243     hide : function(){
41244         this.status_node.removeClass("active");
41245         this.hideAction();
41246         this.fireEvent("deactivate", this.tabPanel, this);
41247     },
41248
41249     hideAction : function(){
41250         this.bodyEl.hide();
41251         this.bodyEl.setStyle("position", "absolute");
41252         this.bodyEl.setLeft("-20000px");
41253         this.bodyEl.setTop("-20000px");
41254     },
41255
41256     showAction : function(){
41257         this.bodyEl.setStyle("position", "relative");
41258         this.bodyEl.setTop("");
41259         this.bodyEl.setLeft("");
41260         this.bodyEl.show();
41261     },
41262
41263     /**
41264      * Set the tooltip for the tab.
41265      * @param {String} tooltip The tab's tooltip
41266      */
41267     setTooltip : function(text){
41268         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41269             this.textEl.dom.qtip = text;
41270             this.textEl.dom.removeAttribute('title');
41271         }else{
41272             this.textEl.dom.title = text;
41273         }
41274     },
41275
41276     onTabClick : function(e){
41277         e.preventDefault();
41278         this.tabPanel.activate(this.id);
41279     },
41280
41281     onTabMouseDown : function(e){
41282         e.preventDefault();
41283         this.tabPanel.activate(this.id);
41284     },
41285 /*
41286     getWidth : function(){
41287         return this.inner.getWidth();
41288     },
41289
41290     setWidth : function(width){
41291         var iwidth = width - this.linode.getPadding("lr");
41292         this.inner.setWidth(iwidth);
41293         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41294         this.linode.setWidth(width);
41295     },
41296 */
41297     /**
41298      * Show or hide the tab
41299      * @param {Boolean} hidden True to hide or false to show.
41300      */
41301     setHidden : function(hidden){
41302         this.hidden = hidden;
41303         this.linode.setStyle("display", hidden ? "none" : "");
41304     },
41305
41306     /**
41307      * Returns true if this tab is "hidden"
41308      * @return {Boolean}
41309      */
41310     isHidden : function(){
41311         return this.hidden;
41312     },
41313
41314     /**
41315      * Returns the text for this tab
41316      * @return {String}
41317      */
41318     getText : function(){
41319         return this.text;
41320     },
41321     /*
41322     autoSize : function(){
41323         //this.el.beginMeasure();
41324         this.textEl.setWidth(1);
41325         /*
41326          *  #2804 [new] Tabs in Roojs
41327          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41328          */
41329         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41330         //this.el.endMeasure();
41331     //},
41332
41333     /**
41334      * Sets the text for the tab (Note: this also sets the tooltip text)
41335      * @param {String} text The tab's text and tooltip
41336      */
41337     setText : function(text){
41338         this.text = text;
41339         this.textEl.update(text);
41340         this.setTooltip(text);
41341         //if(!this.tabPanel.resizeTabs){
41342         //    this.autoSize();
41343         //}
41344     },
41345     /**
41346      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41347      */
41348     activate : function(){
41349         this.tabPanel.activate(this.id);
41350     },
41351
41352     /**
41353      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41354      */
41355     disable : function(){
41356         if(this.tabPanel.active != this){
41357             this.disabled = true;
41358             this.status_node.addClass("disabled");
41359         }
41360     },
41361
41362     /**
41363      * Enables this TabPanelItem if it was previously disabled.
41364      */
41365     enable : function(){
41366         this.disabled = false;
41367         this.status_node.removeClass("disabled");
41368     },
41369
41370     /**
41371      * Sets the content for this TabPanelItem.
41372      * @param {String} content The content
41373      * @param {Boolean} loadScripts true to look for and load scripts
41374      */
41375     setContent : function(content, loadScripts){
41376         this.bodyEl.update(content, loadScripts);
41377     },
41378
41379     /**
41380      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41381      * @return {Roo.UpdateManager} The UpdateManager
41382      */
41383     getUpdateManager : function(){
41384         return this.bodyEl.getUpdateManager();
41385     },
41386
41387     /**
41388      * Set a URL to be used to load the content for this TabPanelItem.
41389      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41390      * @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)
41391      * @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)
41392      * @return {Roo.UpdateManager} The UpdateManager
41393      */
41394     setUrl : function(url, params, loadOnce){
41395         if(this.refreshDelegate){
41396             this.un('activate', this.refreshDelegate);
41397         }
41398         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41399         this.on("activate", this.refreshDelegate);
41400         return this.bodyEl.getUpdateManager();
41401     },
41402
41403     /** @private */
41404     _handleRefresh : function(url, params, loadOnce){
41405         if(!loadOnce || !this.loaded){
41406             var updater = this.bodyEl.getUpdateManager();
41407             updater.update(url, params, this._setLoaded.createDelegate(this));
41408         }
41409     },
41410
41411     /**
41412      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41413      *   Will fail silently if the setUrl method has not been called.
41414      *   This does not activate the panel, just updates its content.
41415      */
41416     refresh : function(){
41417         if(this.refreshDelegate){
41418            this.loaded = false;
41419            this.refreshDelegate();
41420         }
41421     },
41422
41423     /** @private */
41424     _setLoaded : function(){
41425         this.loaded = true;
41426     },
41427
41428     /** @private */
41429     closeClick : function(e){
41430         var o = {};
41431         e.stopEvent();
41432         this.fireEvent("beforeclose", this, o);
41433         if(o.cancel !== true){
41434             this.tabPanel.removeTab(this.id);
41435         }
41436     },
41437     /**
41438      * The text displayed in the tooltip for the close icon.
41439      * @type String
41440      */
41441     closeText : "Close this tab"
41442 });
41443 /**
41444 *    This script refer to:
41445 *    Title: International Telephone Input
41446 *    Author: Jack O'Connor
41447 *    Code version:  v12.1.12
41448 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41449 **/
41450
41451 Roo.bootstrap.PhoneInputData = function() {
41452     var d = [
41453       [
41454         "Afghanistan (‫افغانستان‬‎)",
41455         "af",
41456         "93"
41457       ],
41458       [
41459         "Albania (Shqipëri)",
41460         "al",
41461         "355"
41462       ],
41463       [
41464         "Algeria (‫الجزائر‬‎)",
41465         "dz",
41466         "213"
41467       ],
41468       [
41469         "American Samoa",
41470         "as",
41471         "1684"
41472       ],
41473       [
41474         "Andorra",
41475         "ad",
41476         "376"
41477       ],
41478       [
41479         "Angola",
41480         "ao",
41481         "244"
41482       ],
41483       [
41484         "Anguilla",
41485         "ai",
41486         "1264"
41487       ],
41488       [
41489         "Antigua and Barbuda",
41490         "ag",
41491         "1268"
41492       ],
41493       [
41494         "Argentina",
41495         "ar",
41496         "54"
41497       ],
41498       [
41499         "Armenia (Հայաստան)",
41500         "am",
41501         "374"
41502       ],
41503       [
41504         "Aruba",
41505         "aw",
41506         "297"
41507       ],
41508       [
41509         "Australia",
41510         "au",
41511         "61",
41512         0
41513       ],
41514       [
41515         "Austria (Österreich)",
41516         "at",
41517         "43"
41518       ],
41519       [
41520         "Azerbaijan (Azərbaycan)",
41521         "az",
41522         "994"
41523       ],
41524       [
41525         "Bahamas",
41526         "bs",
41527         "1242"
41528       ],
41529       [
41530         "Bahrain (‫البحرين‬‎)",
41531         "bh",
41532         "973"
41533       ],
41534       [
41535         "Bangladesh (বাংলাদেশ)",
41536         "bd",
41537         "880"
41538       ],
41539       [
41540         "Barbados",
41541         "bb",
41542         "1246"
41543       ],
41544       [
41545         "Belarus (Беларусь)",
41546         "by",
41547         "375"
41548       ],
41549       [
41550         "Belgium (België)",
41551         "be",
41552         "32"
41553       ],
41554       [
41555         "Belize",
41556         "bz",
41557         "501"
41558       ],
41559       [
41560         "Benin (Bénin)",
41561         "bj",
41562         "229"
41563       ],
41564       [
41565         "Bermuda",
41566         "bm",
41567         "1441"
41568       ],
41569       [
41570         "Bhutan (འབྲུག)",
41571         "bt",
41572         "975"
41573       ],
41574       [
41575         "Bolivia",
41576         "bo",
41577         "591"
41578       ],
41579       [
41580         "Bosnia and Herzegovina (Босна и Херцеговина)",
41581         "ba",
41582         "387"
41583       ],
41584       [
41585         "Botswana",
41586         "bw",
41587         "267"
41588       ],
41589       [
41590         "Brazil (Brasil)",
41591         "br",
41592         "55"
41593       ],
41594       [
41595         "British Indian Ocean Territory",
41596         "io",
41597         "246"
41598       ],
41599       [
41600         "British Virgin Islands",
41601         "vg",
41602         "1284"
41603       ],
41604       [
41605         "Brunei",
41606         "bn",
41607         "673"
41608       ],
41609       [
41610         "Bulgaria (България)",
41611         "bg",
41612         "359"
41613       ],
41614       [
41615         "Burkina Faso",
41616         "bf",
41617         "226"
41618       ],
41619       [
41620         "Burundi (Uburundi)",
41621         "bi",
41622         "257"
41623       ],
41624       [
41625         "Cambodia (កម្ពុជា)",
41626         "kh",
41627         "855"
41628       ],
41629       [
41630         "Cameroon (Cameroun)",
41631         "cm",
41632         "237"
41633       ],
41634       [
41635         "Canada",
41636         "ca",
41637         "1",
41638         1,
41639         ["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"]
41640       ],
41641       [
41642         "Cape Verde (Kabu Verdi)",
41643         "cv",
41644         "238"
41645       ],
41646       [
41647         "Caribbean Netherlands",
41648         "bq",
41649         "599",
41650         1
41651       ],
41652       [
41653         "Cayman Islands",
41654         "ky",
41655         "1345"
41656       ],
41657       [
41658         "Central African Republic (République centrafricaine)",
41659         "cf",
41660         "236"
41661       ],
41662       [
41663         "Chad (Tchad)",
41664         "td",
41665         "235"
41666       ],
41667       [
41668         "Chile",
41669         "cl",
41670         "56"
41671       ],
41672       [
41673         "China (中国)",
41674         "cn",
41675         "86"
41676       ],
41677       [
41678         "Christmas Island",
41679         "cx",
41680         "61",
41681         2
41682       ],
41683       [
41684         "Cocos (Keeling) Islands",
41685         "cc",
41686         "61",
41687         1
41688       ],
41689       [
41690         "Colombia",
41691         "co",
41692         "57"
41693       ],
41694       [
41695         "Comoros (‫جزر القمر‬‎)",
41696         "km",
41697         "269"
41698       ],
41699       [
41700         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41701         "cd",
41702         "243"
41703       ],
41704       [
41705         "Congo (Republic) (Congo-Brazzaville)",
41706         "cg",
41707         "242"
41708       ],
41709       [
41710         "Cook Islands",
41711         "ck",
41712         "682"
41713       ],
41714       [
41715         "Costa Rica",
41716         "cr",
41717         "506"
41718       ],
41719       [
41720         "Côte d’Ivoire",
41721         "ci",
41722         "225"
41723       ],
41724       [
41725         "Croatia (Hrvatska)",
41726         "hr",
41727         "385"
41728       ],
41729       [
41730         "Cuba",
41731         "cu",
41732         "53"
41733       ],
41734       [
41735         "Curaçao",
41736         "cw",
41737         "599",
41738         0
41739       ],
41740       [
41741         "Cyprus (Κύπρος)",
41742         "cy",
41743         "357"
41744       ],
41745       [
41746         "Czech Republic (Česká republika)",
41747         "cz",
41748         "420"
41749       ],
41750       [
41751         "Denmark (Danmark)",
41752         "dk",
41753         "45"
41754       ],
41755       [
41756         "Djibouti",
41757         "dj",
41758         "253"
41759       ],
41760       [
41761         "Dominica",
41762         "dm",
41763         "1767"
41764       ],
41765       [
41766         "Dominican Republic (República Dominicana)",
41767         "do",
41768         "1",
41769         2,
41770         ["809", "829", "849"]
41771       ],
41772       [
41773         "Ecuador",
41774         "ec",
41775         "593"
41776       ],
41777       [
41778         "Egypt (‫مصر‬‎)",
41779         "eg",
41780         "20"
41781       ],
41782       [
41783         "El Salvador",
41784         "sv",
41785         "503"
41786       ],
41787       [
41788         "Equatorial Guinea (Guinea Ecuatorial)",
41789         "gq",
41790         "240"
41791       ],
41792       [
41793         "Eritrea",
41794         "er",
41795         "291"
41796       ],
41797       [
41798         "Estonia (Eesti)",
41799         "ee",
41800         "372"
41801       ],
41802       [
41803         "Ethiopia",
41804         "et",
41805         "251"
41806       ],
41807       [
41808         "Falkland Islands (Islas Malvinas)",
41809         "fk",
41810         "500"
41811       ],
41812       [
41813         "Faroe Islands (Føroyar)",
41814         "fo",
41815         "298"
41816       ],
41817       [
41818         "Fiji",
41819         "fj",
41820         "679"
41821       ],
41822       [
41823         "Finland (Suomi)",
41824         "fi",
41825         "358",
41826         0
41827       ],
41828       [
41829         "France",
41830         "fr",
41831         "33"
41832       ],
41833       [
41834         "French Guiana (Guyane française)",
41835         "gf",
41836         "594"
41837       ],
41838       [
41839         "French Polynesia (Polynésie française)",
41840         "pf",
41841         "689"
41842       ],
41843       [
41844         "Gabon",
41845         "ga",
41846         "241"
41847       ],
41848       [
41849         "Gambia",
41850         "gm",
41851         "220"
41852       ],
41853       [
41854         "Georgia (საქართველო)",
41855         "ge",
41856         "995"
41857       ],
41858       [
41859         "Germany (Deutschland)",
41860         "de",
41861         "49"
41862       ],
41863       [
41864         "Ghana (Gaana)",
41865         "gh",
41866         "233"
41867       ],
41868       [
41869         "Gibraltar",
41870         "gi",
41871         "350"
41872       ],
41873       [
41874         "Greece (Ελλάδα)",
41875         "gr",
41876         "30"
41877       ],
41878       [
41879         "Greenland (Kalaallit Nunaat)",
41880         "gl",
41881         "299"
41882       ],
41883       [
41884         "Grenada",
41885         "gd",
41886         "1473"
41887       ],
41888       [
41889         "Guadeloupe",
41890         "gp",
41891         "590",
41892         0
41893       ],
41894       [
41895         "Guam",
41896         "gu",
41897         "1671"
41898       ],
41899       [
41900         "Guatemala",
41901         "gt",
41902         "502"
41903       ],
41904       [
41905         "Guernsey",
41906         "gg",
41907         "44",
41908         1
41909       ],
41910       [
41911         "Guinea (Guinée)",
41912         "gn",
41913         "224"
41914       ],
41915       [
41916         "Guinea-Bissau (Guiné Bissau)",
41917         "gw",
41918         "245"
41919       ],
41920       [
41921         "Guyana",
41922         "gy",
41923         "592"
41924       ],
41925       [
41926         "Haiti",
41927         "ht",
41928         "509"
41929       ],
41930       [
41931         "Honduras",
41932         "hn",
41933         "504"
41934       ],
41935       [
41936         "Hong Kong (香港)",
41937         "hk",
41938         "852"
41939       ],
41940       [
41941         "Hungary (Magyarország)",
41942         "hu",
41943         "36"
41944       ],
41945       [
41946         "Iceland (Ísland)",
41947         "is",
41948         "354"
41949       ],
41950       [
41951         "India (भारत)",
41952         "in",
41953         "91"
41954       ],
41955       [
41956         "Indonesia",
41957         "id",
41958         "62"
41959       ],
41960       [
41961         "Iran (‫ایران‬‎)",
41962         "ir",
41963         "98"
41964       ],
41965       [
41966         "Iraq (‫العراق‬‎)",
41967         "iq",
41968         "964"
41969       ],
41970       [
41971         "Ireland",
41972         "ie",
41973         "353"
41974       ],
41975       [
41976         "Isle of Man",
41977         "im",
41978         "44",
41979         2
41980       ],
41981       [
41982         "Israel (‫ישראל‬‎)",
41983         "il",
41984         "972"
41985       ],
41986       [
41987         "Italy (Italia)",
41988         "it",
41989         "39",
41990         0
41991       ],
41992       [
41993         "Jamaica",
41994         "jm",
41995         "1876"
41996       ],
41997       [
41998         "Japan (日本)",
41999         "jp",
42000         "81"
42001       ],
42002       [
42003         "Jersey",
42004         "je",
42005         "44",
42006         3
42007       ],
42008       [
42009         "Jordan (‫الأردن‬‎)",
42010         "jo",
42011         "962"
42012       ],
42013       [
42014         "Kazakhstan (Казахстан)",
42015         "kz",
42016         "7",
42017         1
42018       ],
42019       [
42020         "Kenya",
42021         "ke",
42022         "254"
42023       ],
42024       [
42025         "Kiribati",
42026         "ki",
42027         "686"
42028       ],
42029       [
42030         "Kosovo",
42031         "xk",
42032         "383"
42033       ],
42034       [
42035         "Kuwait (‫الكويت‬‎)",
42036         "kw",
42037         "965"
42038       ],
42039       [
42040         "Kyrgyzstan (Кыргызстан)",
42041         "kg",
42042         "996"
42043       ],
42044       [
42045         "Laos (ລາວ)",
42046         "la",
42047         "856"
42048       ],
42049       [
42050         "Latvia (Latvija)",
42051         "lv",
42052         "371"
42053       ],
42054       [
42055         "Lebanon (‫لبنان‬‎)",
42056         "lb",
42057         "961"
42058       ],
42059       [
42060         "Lesotho",
42061         "ls",
42062         "266"
42063       ],
42064       [
42065         "Liberia",
42066         "lr",
42067         "231"
42068       ],
42069       [
42070         "Libya (‫ليبيا‬‎)",
42071         "ly",
42072         "218"
42073       ],
42074       [
42075         "Liechtenstein",
42076         "li",
42077         "423"
42078       ],
42079       [
42080         "Lithuania (Lietuva)",
42081         "lt",
42082         "370"
42083       ],
42084       [
42085         "Luxembourg",
42086         "lu",
42087         "352"
42088       ],
42089       [
42090         "Macau (澳門)",
42091         "mo",
42092         "853"
42093       ],
42094       [
42095         "Macedonia (FYROM) (Македонија)",
42096         "mk",
42097         "389"
42098       ],
42099       [
42100         "Madagascar (Madagasikara)",
42101         "mg",
42102         "261"
42103       ],
42104       [
42105         "Malawi",
42106         "mw",
42107         "265"
42108       ],
42109       [
42110         "Malaysia",
42111         "my",
42112         "60"
42113       ],
42114       [
42115         "Maldives",
42116         "mv",
42117         "960"
42118       ],
42119       [
42120         "Mali",
42121         "ml",
42122         "223"
42123       ],
42124       [
42125         "Malta",
42126         "mt",
42127         "356"
42128       ],
42129       [
42130         "Marshall Islands",
42131         "mh",
42132         "692"
42133       ],
42134       [
42135         "Martinique",
42136         "mq",
42137         "596"
42138       ],
42139       [
42140         "Mauritania (‫موريتانيا‬‎)",
42141         "mr",
42142         "222"
42143       ],
42144       [
42145         "Mauritius (Moris)",
42146         "mu",
42147         "230"
42148       ],
42149       [
42150         "Mayotte",
42151         "yt",
42152         "262",
42153         1
42154       ],
42155       [
42156         "Mexico (México)",
42157         "mx",
42158         "52"
42159       ],
42160       [
42161         "Micronesia",
42162         "fm",
42163         "691"
42164       ],
42165       [
42166         "Moldova (Republica Moldova)",
42167         "md",
42168         "373"
42169       ],
42170       [
42171         "Monaco",
42172         "mc",
42173         "377"
42174       ],
42175       [
42176         "Mongolia (Монгол)",
42177         "mn",
42178         "976"
42179       ],
42180       [
42181         "Montenegro (Crna Gora)",
42182         "me",
42183         "382"
42184       ],
42185       [
42186         "Montserrat",
42187         "ms",
42188         "1664"
42189       ],
42190       [
42191         "Morocco (‫المغرب‬‎)",
42192         "ma",
42193         "212",
42194         0
42195       ],
42196       [
42197         "Mozambique (Moçambique)",
42198         "mz",
42199         "258"
42200       ],
42201       [
42202         "Myanmar (Burma) (မြန်မာ)",
42203         "mm",
42204         "95"
42205       ],
42206       [
42207         "Namibia (Namibië)",
42208         "na",
42209         "264"
42210       ],
42211       [
42212         "Nauru",
42213         "nr",
42214         "674"
42215       ],
42216       [
42217         "Nepal (नेपाल)",
42218         "np",
42219         "977"
42220       ],
42221       [
42222         "Netherlands (Nederland)",
42223         "nl",
42224         "31"
42225       ],
42226       [
42227         "New Caledonia (Nouvelle-Calédonie)",
42228         "nc",
42229         "687"
42230       ],
42231       [
42232         "New Zealand",
42233         "nz",
42234         "64"
42235       ],
42236       [
42237         "Nicaragua",
42238         "ni",
42239         "505"
42240       ],
42241       [
42242         "Niger (Nijar)",
42243         "ne",
42244         "227"
42245       ],
42246       [
42247         "Nigeria",
42248         "ng",
42249         "234"
42250       ],
42251       [
42252         "Niue",
42253         "nu",
42254         "683"
42255       ],
42256       [
42257         "Norfolk Island",
42258         "nf",
42259         "672"
42260       ],
42261       [
42262         "North Korea (조선 민주주의 인민 공화국)",
42263         "kp",
42264         "850"
42265       ],
42266       [
42267         "Northern Mariana Islands",
42268         "mp",
42269         "1670"
42270       ],
42271       [
42272         "Norway (Norge)",
42273         "no",
42274         "47",
42275         0
42276       ],
42277       [
42278         "Oman (‫عُمان‬‎)",
42279         "om",
42280         "968"
42281       ],
42282       [
42283         "Pakistan (‫پاکستان‬‎)",
42284         "pk",
42285         "92"
42286       ],
42287       [
42288         "Palau",
42289         "pw",
42290         "680"
42291       ],
42292       [
42293         "Palestine (‫فلسطين‬‎)",
42294         "ps",
42295         "970"
42296       ],
42297       [
42298         "Panama (Panamá)",
42299         "pa",
42300         "507"
42301       ],
42302       [
42303         "Papua New Guinea",
42304         "pg",
42305         "675"
42306       ],
42307       [
42308         "Paraguay",
42309         "py",
42310         "595"
42311       ],
42312       [
42313         "Peru (Perú)",
42314         "pe",
42315         "51"
42316       ],
42317       [
42318         "Philippines",
42319         "ph",
42320         "63"
42321       ],
42322       [
42323         "Poland (Polska)",
42324         "pl",
42325         "48"
42326       ],
42327       [
42328         "Portugal",
42329         "pt",
42330         "351"
42331       ],
42332       [
42333         "Puerto Rico",
42334         "pr",
42335         "1",
42336         3,
42337         ["787", "939"]
42338       ],
42339       [
42340         "Qatar (‫قطر‬‎)",
42341         "qa",
42342         "974"
42343       ],
42344       [
42345         "Réunion (La Réunion)",
42346         "re",
42347         "262",
42348         0
42349       ],
42350       [
42351         "Romania (România)",
42352         "ro",
42353         "40"
42354       ],
42355       [
42356         "Russia (Россия)",
42357         "ru",
42358         "7",
42359         0
42360       ],
42361       [
42362         "Rwanda",
42363         "rw",
42364         "250"
42365       ],
42366       [
42367         "Saint Barthélemy",
42368         "bl",
42369         "590",
42370         1
42371       ],
42372       [
42373         "Saint Helena",
42374         "sh",
42375         "290"
42376       ],
42377       [
42378         "Saint Kitts and Nevis",
42379         "kn",
42380         "1869"
42381       ],
42382       [
42383         "Saint Lucia",
42384         "lc",
42385         "1758"
42386       ],
42387       [
42388         "Saint Martin (Saint-Martin (partie française))",
42389         "mf",
42390         "590",
42391         2
42392       ],
42393       [
42394         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42395         "pm",
42396         "508"
42397       ],
42398       [
42399         "Saint Vincent and the Grenadines",
42400         "vc",
42401         "1784"
42402       ],
42403       [
42404         "Samoa",
42405         "ws",
42406         "685"
42407       ],
42408       [
42409         "San Marino",
42410         "sm",
42411         "378"
42412       ],
42413       [
42414         "São Tomé and Príncipe (São Tomé e Príncipe)",
42415         "st",
42416         "239"
42417       ],
42418       [
42419         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42420         "sa",
42421         "966"
42422       ],
42423       [
42424         "Senegal (Sénégal)",
42425         "sn",
42426         "221"
42427       ],
42428       [
42429         "Serbia (Србија)",
42430         "rs",
42431         "381"
42432       ],
42433       [
42434         "Seychelles",
42435         "sc",
42436         "248"
42437       ],
42438       [
42439         "Sierra Leone",
42440         "sl",
42441         "232"
42442       ],
42443       [
42444         "Singapore",
42445         "sg",
42446         "65"
42447       ],
42448       [
42449         "Sint Maarten",
42450         "sx",
42451         "1721"
42452       ],
42453       [
42454         "Slovakia (Slovensko)",
42455         "sk",
42456         "421"
42457       ],
42458       [
42459         "Slovenia (Slovenija)",
42460         "si",
42461         "386"
42462       ],
42463       [
42464         "Solomon Islands",
42465         "sb",
42466         "677"
42467       ],
42468       [
42469         "Somalia (Soomaaliya)",
42470         "so",
42471         "252"
42472       ],
42473       [
42474         "South Africa",
42475         "za",
42476         "27"
42477       ],
42478       [
42479         "South Korea (대한민국)",
42480         "kr",
42481         "82"
42482       ],
42483       [
42484         "South Sudan (‫جنوب السودان‬‎)",
42485         "ss",
42486         "211"
42487       ],
42488       [
42489         "Spain (España)",
42490         "es",
42491         "34"
42492       ],
42493       [
42494         "Sri Lanka (ශ්‍රී ලංකාව)",
42495         "lk",
42496         "94"
42497       ],
42498       [
42499         "Sudan (‫السودان‬‎)",
42500         "sd",
42501         "249"
42502       ],
42503       [
42504         "Suriname",
42505         "sr",
42506         "597"
42507       ],
42508       [
42509         "Svalbard and Jan Mayen",
42510         "sj",
42511         "47",
42512         1
42513       ],
42514       [
42515         "Swaziland",
42516         "sz",
42517         "268"
42518       ],
42519       [
42520         "Sweden (Sverige)",
42521         "se",
42522         "46"
42523       ],
42524       [
42525         "Switzerland (Schweiz)",
42526         "ch",
42527         "41"
42528       ],
42529       [
42530         "Syria (‫سوريا‬‎)",
42531         "sy",
42532         "963"
42533       ],
42534       [
42535         "Taiwan (台灣)",
42536         "tw",
42537         "886"
42538       ],
42539       [
42540         "Tajikistan",
42541         "tj",
42542         "992"
42543       ],
42544       [
42545         "Tanzania",
42546         "tz",
42547         "255"
42548       ],
42549       [
42550         "Thailand (ไทย)",
42551         "th",
42552         "66"
42553       ],
42554       [
42555         "Timor-Leste",
42556         "tl",
42557         "670"
42558       ],
42559       [
42560         "Togo",
42561         "tg",
42562         "228"
42563       ],
42564       [
42565         "Tokelau",
42566         "tk",
42567         "690"
42568       ],
42569       [
42570         "Tonga",
42571         "to",
42572         "676"
42573       ],
42574       [
42575         "Trinidad and Tobago",
42576         "tt",
42577         "1868"
42578       ],
42579       [
42580         "Tunisia (‫تونس‬‎)",
42581         "tn",
42582         "216"
42583       ],
42584       [
42585         "Turkey (Türkiye)",
42586         "tr",
42587         "90"
42588       ],
42589       [
42590         "Turkmenistan",
42591         "tm",
42592         "993"
42593       ],
42594       [
42595         "Turks and Caicos Islands",
42596         "tc",
42597         "1649"
42598       ],
42599       [
42600         "Tuvalu",
42601         "tv",
42602         "688"
42603       ],
42604       [
42605         "U.S. Virgin Islands",
42606         "vi",
42607         "1340"
42608       ],
42609       [
42610         "Uganda",
42611         "ug",
42612         "256"
42613       ],
42614       [
42615         "Ukraine (Україна)",
42616         "ua",
42617         "380"
42618       ],
42619       [
42620         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42621         "ae",
42622         "971"
42623       ],
42624       [
42625         "United Kingdom",
42626         "gb",
42627         "44",
42628         0
42629       ],
42630       [
42631         "United States",
42632         "us",
42633         "1",
42634         0
42635       ],
42636       [
42637         "Uruguay",
42638         "uy",
42639         "598"
42640       ],
42641       [
42642         "Uzbekistan (Oʻzbekiston)",
42643         "uz",
42644         "998"
42645       ],
42646       [
42647         "Vanuatu",
42648         "vu",
42649         "678"
42650       ],
42651       [
42652         "Vatican City (Città del Vaticano)",
42653         "va",
42654         "39",
42655         1
42656       ],
42657       [
42658         "Venezuela",
42659         "ve",
42660         "58"
42661       ],
42662       [
42663         "Vietnam (Việt Nam)",
42664         "vn",
42665         "84"
42666       ],
42667       [
42668         "Wallis and Futuna (Wallis-et-Futuna)",
42669         "wf",
42670         "681"
42671       ],
42672       [
42673         "Western Sahara (‫الصحراء الغربية‬‎)",
42674         "eh",
42675         "212",
42676         1
42677       ],
42678       [
42679         "Yemen (‫اليمن‬‎)",
42680         "ye",
42681         "967"
42682       ],
42683       [
42684         "Zambia",
42685         "zm",
42686         "260"
42687       ],
42688       [
42689         "Zimbabwe",
42690         "zw",
42691         "263"
42692       ],
42693       [
42694         "Åland Islands",
42695         "ax",
42696         "358",
42697         1
42698       ]
42699   ];
42700   
42701   return d;
42702 }/**
42703 *    This script refer to:
42704 *    Title: International Telephone Input
42705 *    Author: Jack O'Connor
42706 *    Code version:  v12.1.12
42707 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42708 **/
42709
42710 /**
42711  * @class Roo.bootstrap.PhoneInput
42712  * @extends Roo.bootstrap.TriggerField
42713  * An input with International dial-code selection
42714  
42715  * @cfg {String} defaultDialCode default '+852'
42716  * @cfg {Array} preferedCountries default []
42717   
42718  * @constructor
42719  * Create a new PhoneInput.
42720  * @param {Object} config Configuration options
42721  */
42722
42723 Roo.bootstrap.PhoneInput = function(config) {
42724     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42725 };
42726
42727 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42728         
42729         listWidth: undefined,
42730         
42731         selectedClass: 'active',
42732         
42733         invalidClass : "has-warning",
42734         
42735         validClass: 'has-success',
42736         
42737         allowed: '0123456789',
42738         
42739         max_length: 15,
42740         
42741         /**
42742          * @cfg {String} defaultDialCode The default dial code when initializing the input
42743          */
42744         defaultDialCode: '+852',
42745         
42746         /**
42747          * @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
42748          */
42749         preferedCountries: false,
42750         
42751         getAutoCreate : function()
42752         {
42753             var data = Roo.bootstrap.PhoneInputData();
42754             var align = this.labelAlign || this.parentLabelAlign();
42755             var id = Roo.id();
42756             
42757             this.allCountries = [];
42758             this.dialCodeMapping = [];
42759             
42760             for (var i = 0; i < data.length; i++) {
42761               var c = data[i];
42762               this.allCountries[i] = {
42763                 name: c[0],
42764                 iso2: c[1],
42765                 dialCode: c[2],
42766                 priority: c[3] || 0,
42767                 areaCodes: c[4] || null
42768               };
42769               this.dialCodeMapping[c[2]] = {
42770                   name: c[0],
42771                   iso2: c[1],
42772                   priority: c[3] || 0,
42773                   areaCodes: c[4] || null
42774               };
42775             }
42776             
42777             var cfg = {
42778                 cls: 'form-group',
42779                 cn: []
42780             };
42781             
42782             var input =  {
42783                 tag: 'input',
42784                 id : id,
42785                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42786                 maxlength: this.max_length,
42787                 cls : 'form-control tel-input',
42788                 autocomplete: 'new-password'
42789             };
42790             
42791             var hiddenInput = {
42792                 tag: 'input',
42793                 type: 'hidden',
42794                 cls: 'hidden-tel-input'
42795             };
42796             
42797             if (this.name) {
42798                 hiddenInput.name = this.name;
42799             }
42800             
42801             if (this.disabled) {
42802                 input.disabled = true;
42803             }
42804             
42805             var flag_container = {
42806                 tag: 'div',
42807                 cls: 'flag-box',
42808                 cn: [
42809                     {
42810                         tag: 'div',
42811                         cls: 'flag'
42812                     },
42813                     {
42814                         tag: 'div',
42815                         cls: 'caret'
42816                     }
42817                 ]
42818             };
42819             
42820             var box = {
42821                 tag: 'div',
42822                 cls: this.hasFeedback ? 'has-feedback' : '',
42823                 cn: [
42824                     hiddenInput,
42825                     input,
42826                     {
42827                         tag: 'input',
42828                         cls: 'dial-code-holder',
42829                         disabled: true
42830                     }
42831                 ]
42832             };
42833             
42834             var container = {
42835                 cls: 'roo-select2-container input-group',
42836                 cn: [
42837                     flag_container,
42838                     box
42839                 ]
42840             };
42841             
42842             if (this.fieldLabel.length) {
42843                 var indicator = {
42844                     tag: 'i',
42845                     tooltip: 'This field is required'
42846                 };
42847                 
42848                 var label = {
42849                     tag: 'label',
42850                     'for':  id,
42851                     cls: 'control-label',
42852                     cn: []
42853                 };
42854                 
42855                 var label_text = {
42856                     tag: 'span',
42857                     html: this.fieldLabel
42858                 };
42859                 
42860                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42861                 label.cn = [
42862                     indicator,
42863                     label_text
42864                 ];
42865                 
42866                 if(this.indicatorpos == 'right') {
42867                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42868                     label.cn = [
42869                         label_text,
42870                         indicator
42871                     ];
42872                 }
42873                 
42874                 if(align == 'left') {
42875                     container = {
42876                         tag: 'div',
42877                         cn: [
42878                             container
42879                         ]
42880                     };
42881                     
42882                     if(this.labelWidth > 12){
42883                         label.style = "width: " + this.labelWidth + 'px';
42884                     }
42885                     if(this.labelWidth < 13 && this.labelmd == 0){
42886                         this.labelmd = this.labelWidth;
42887                     }
42888                     if(this.labellg > 0){
42889                         label.cls += ' col-lg-' + this.labellg;
42890                         input.cls += ' col-lg-' + (12 - this.labellg);
42891                     }
42892                     if(this.labelmd > 0){
42893                         label.cls += ' col-md-' + this.labelmd;
42894                         container.cls += ' col-md-' + (12 - this.labelmd);
42895                     }
42896                     if(this.labelsm > 0){
42897                         label.cls += ' col-sm-' + this.labelsm;
42898                         container.cls += ' col-sm-' + (12 - this.labelsm);
42899                     }
42900                     if(this.labelxs > 0){
42901                         label.cls += ' col-xs-' + this.labelxs;
42902                         container.cls += ' col-xs-' + (12 - this.labelxs);
42903                     }
42904                 }
42905             }
42906             
42907             cfg.cn = [
42908                 label,
42909                 container
42910             ];
42911             
42912             var settings = this;
42913             
42914             ['xs','sm','md','lg'].map(function(size){
42915                 if (settings[size]) {
42916                     cfg.cls += ' col-' + size + '-' + settings[size];
42917                 }
42918             });
42919             
42920             this.store = new Roo.data.Store({
42921                 proxy : new Roo.data.MemoryProxy({}),
42922                 reader : new Roo.data.JsonReader({
42923                     fields : [
42924                         {
42925                             'name' : 'name',
42926                             'type' : 'string'
42927                         },
42928                         {
42929                             'name' : 'iso2',
42930                             'type' : 'string'
42931                         },
42932                         {
42933                             'name' : 'dialCode',
42934                             'type' : 'string'
42935                         },
42936                         {
42937                             'name' : 'priority',
42938                             'type' : 'string'
42939                         },
42940                         {
42941                             'name' : 'areaCodes',
42942                             'type' : 'string'
42943                         }
42944                     ]
42945                 })
42946             });
42947             
42948             if(!this.preferedCountries) {
42949                 this.preferedCountries = [
42950                     'hk',
42951                     'gb',
42952                     'us'
42953                 ];
42954             }
42955             
42956             var p = this.preferedCountries.reverse();
42957             
42958             if(p) {
42959                 for (var i = 0; i < p.length; i++) {
42960                     for (var j = 0; j < this.allCountries.length; j++) {
42961                         if(this.allCountries[j].iso2 == p[i]) {
42962                             var t = this.allCountries[j];
42963                             this.allCountries.splice(j,1);
42964                             this.allCountries.unshift(t);
42965                         }
42966                     } 
42967                 }
42968             }
42969             
42970             this.store.proxy.data = {
42971                 success: true,
42972                 data: this.allCountries
42973             };
42974             
42975             return cfg;
42976         },
42977         
42978         initEvents : function()
42979         {
42980             this.createList();
42981             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42982             
42983             this.indicator = this.indicatorEl();
42984             this.flag = this.flagEl();
42985             this.dialCodeHolder = this.dialCodeHolderEl();
42986             
42987             this.trigger = this.el.select('div.flag-box',true).first();
42988             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42989             
42990             var _this = this;
42991             
42992             (function(){
42993                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42994                 _this.list.setWidth(lw);
42995             }).defer(100);
42996             
42997             this.list.on('mouseover', this.onViewOver, this);
42998             this.list.on('mousemove', this.onViewMove, this);
42999             this.inputEl().on("keyup", this.onKeyUp, this);
43000             this.inputEl().on("keypress", this.onKeyPress, this);
43001             
43002             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
43003
43004             this.view = new Roo.View(this.list, this.tpl, {
43005                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
43006             });
43007             
43008             this.view.on('click', this.onViewClick, this);
43009             this.setValue(this.defaultDialCode);
43010         },
43011         
43012         onTriggerClick : function(e)
43013         {
43014             Roo.log('trigger click');
43015             if(this.disabled){
43016                 return;
43017             }
43018             
43019             if(this.isExpanded()){
43020                 this.collapse();
43021                 this.hasFocus = false;
43022             }else {
43023                 this.store.load({});
43024                 this.hasFocus = true;
43025                 this.expand();
43026             }
43027         },
43028         
43029         isExpanded : function()
43030         {
43031             return this.list.isVisible();
43032         },
43033         
43034         collapse : function()
43035         {
43036             if(!this.isExpanded()){
43037                 return;
43038             }
43039             this.list.hide();
43040             Roo.get(document).un('mousedown', this.collapseIf, this);
43041             Roo.get(document).un('mousewheel', this.collapseIf, this);
43042             this.fireEvent('collapse', this);
43043             this.validate();
43044         },
43045         
43046         expand : function()
43047         {
43048             Roo.log('expand');
43049
43050             if(this.isExpanded() || !this.hasFocus){
43051                 return;
43052             }
43053             
43054             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43055             this.list.setWidth(lw);
43056             
43057             this.list.show();
43058             this.restrictHeight();
43059             
43060             Roo.get(document).on('mousedown', this.collapseIf, this);
43061             Roo.get(document).on('mousewheel', this.collapseIf, this);
43062             
43063             this.fireEvent('expand', this);
43064         },
43065         
43066         restrictHeight : function()
43067         {
43068             this.list.alignTo(this.inputEl(), this.listAlign);
43069             this.list.alignTo(this.inputEl(), this.listAlign);
43070         },
43071         
43072         onViewOver : function(e, t)
43073         {
43074             if(this.inKeyMode){
43075                 return;
43076             }
43077             var item = this.view.findItemFromChild(t);
43078             
43079             if(item){
43080                 var index = this.view.indexOf(item);
43081                 this.select(index, false);
43082             }
43083         },
43084
43085         // private
43086         onViewClick : function(view, doFocus, el, e)
43087         {
43088             var index = this.view.getSelectedIndexes()[0];
43089             
43090             var r = this.store.getAt(index);
43091             
43092             if(r){
43093                 this.onSelect(r, index);
43094             }
43095             if(doFocus !== false && !this.blockFocus){
43096                 this.inputEl().focus();
43097             }
43098         },
43099         
43100         onViewMove : function(e, t)
43101         {
43102             this.inKeyMode = false;
43103         },
43104         
43105         select : function(index, scrollIntoView)
43106         {
43107             this.selectedIndex = index;
43108             this.view.select(index);
43109             if(scrollIntoView !== false){
43110                 var el = this.view.getNode(index);
43111                 if(el){
43112                     this.list.scrollChildIntoView(el, false);
43113                 }
43114             }
43115         },
43116         
43117         createList : function()
43118         {
43119             this.list = Roo.get(document.body).createChild({
43120                 tag: 'ul',
43121                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43122                 style: 'display:none'
43123             });
43124             
43125             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43126         },
43127         
43128         collapseIf : function(e)
43129         {
43130             var in_combo  = e.within(this.el);
43131             var in_list =  e.within(this.list);
43132             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43133             
43134             if (in_combo || in_list || is_list) {
43135                 return;
43136             }
43137             this.collapse();
43138         },
43139         
43140         onSelect : function(record, index)
43141         {
43142             if(this.fireEvent('beforeselect', this, record, index) !== false){
43143                 
43144                 this.setFlagClass(record.data.iso2);
43145                 this.setDialCode(record.data.dialCode);
43146                 this.hasFocus = false;
43147                 this.collapse();
43148                 this.fireEvent('select', this, record, index);
43149             }
43150         },
43151         
43152         flagEl : function()
43153         {
43154             var flag = this.el.select('div.flag',true).first();
43155             if(!flag){
43156                 return false;
43157             }
43158             return flag;
43159         },
43160         
43161         dialCodeHolderEl : function()
43162         {
43163             var d = this.el.select('input.dial-code-holder',true).first();
43164             if(!d){
43165                 return false;
43166             }
43167             return d;
43168         },
43169         
43170         setDialCode : function(v)
43171         {
43172             this.dialCodeHolder.dom.value = '+'+v;
43173         },
43174         
43175         setFlagClass : function(n)
43176         {
43177             this.flag.dom.className = 'flag '+n;
43178         },
43179         
43180         getValue : function()
43181         {
43182             var v = this.inputEl().getValue();
43183             if(this.dialCodeHolder) {
43184                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43185             }
43186             return v;
43187         },
43188         
43189         setValue : function(v)
43190         {
43191             var d = this.getDialCode(v);
43192             
43193             //invalid dial code
43194             if(v.length == 0 || !d || d.length == 0) {
43195                 if(this.rendered){
43196                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43197                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43198                 }
43199                 return;
43200             }
43201             
43202             //valid dial code
43203             this.setFlagClass(this.dialCodeMapping[d].iso2);
43204             this.setDialCode(d);
43205             this.inputEl().dom.value = v.replace('+'+d,'');
43206             this.hiddenEl().dom.value = this.getValue();
43207             
43208             this.validate();
43209         },
43210         
43211         getDialCode : function(v)
43212         {
43213             v = v ||  '';
43214             
43215             if (v.length == 0) {
43216                 return this.dialCodeHolder.dom.value;
43217             }
43218             
43219             var dialCode = "";
43220             if (v.charAt(0) != "+") {
43221                 return false;
43222             }
43223             var numericChars = "";
43224             for (var i = 1; i < v.length; i++) {
43225               var c = v.charAt(i);
43226               if (!isNaN(c)) {
43227                 numericChars += c;
43228                 if (this.dialCodeMapping[numericChars]) {
43229                   dialCode = v.substr(1, i);
43230                 }
43231                 if (numericChars.length == 4) {
43232                   break;
43233                 }
43234               }
43235             }
43236             return dialCode;
43237         },
43238         
43239         reset : function()
43240         {
43241             this.setValue(this.defaultDialCode);
43242             this.validate();
43243         },
43244         
43245         hiddenEl : function()
43246         {
43247             return this.el.select('input.hidden-tel-input',true).first();
43248         },
43249         
43250         // after setting val
43251         onKeyUp : function(e){
43252             this.setValue(this.getValue());
43253         },
43254         
43255         onKeyPress : function(e){
43256             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43257                 e.stopEvent();
43258             }
43259         }
43260         
43261 });
43262 /**
43263  * @class Roo.bootstrap.MoneyField
43264  * @extends Roo.bootstrap.ComboBox
43265  * Bootstrap MoneyField class
43266  * 
43267  * @constructor
43268  * Create a new MoneyField.
43269  * @param {Object} config Configuration options
43270  */
43271
43272 Roo.bootstrap.MoneyField = function(config) {
43273     
43274     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43275     
43276 };
43277
43278 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43279     
43280     /**
43281      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43282      */
43283     allowDecimals : true,
43284     /**
43285      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43286      */
43287     decimalSeparator : ".",
43288     /**
43289      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43290      */
43291     decimalPrecision : 0,
43292     /**
43293      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43294      */
43295     allowNegative : true,
43296     /**
43297      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43298      */
43299     allowZero: true,
43300     /**
43301      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43302      */
43303     minValue : Number.NEGATIVE_INFINITY,
43304     /**
43305      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43306      */
43307     maxValue : Number.MAX_VALUE,
43308     /**
43309      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43310      */
43311     minText : "The minimum value for this field is {0}",
43312     /**
43313      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43314      */
43315     maxText : "The maximum value for this field is {0}",
43316     /**
43317      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43318      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43319      */
43320     nanText : "{0} is not a valid number",
43321     /**
43322      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43323      */
43324     castInt : true,
43325     /**
43326      * @cfg {String} defaults currency of the MoneyField
43327      * value should be in lkey
43328      */
43329     defaultCurrency : false,
43330     /**
43331      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43332      */
43333     thousandsDelimiter : false,
43334     /**
43335      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43336      */
43337     max_length: false,
43338     
43339     inputlg : 9,
43340     inputmd : 9,
43341     inputsm : 9,
43342     inputxs : 6,
43343     
43344     store : false,
43345     
43346     getAutoCreate : function()
43347     {
43348         var align = this.labelAlign || this.parentLabelAlign();
43349         
43350         var id = Roo.id();
43351
43352         var cfg = {
43353             cls: 'form-group',
43354             cn: []
43355         };
43356
43357         var input =  {
43358             tag: 'input',
43359             id : id,
43360             cls : 'form-control roo-money-amount-input',
43361             autocomplete: 'new-password'
43362         };
43363         
43364         var hiddenInput = {
43365             tag: 'input',
43366             type: 'hidden',
43367             id: Roo.id(),
43368             cls: 'hidden-number-input'
43369         };
43370         
43371         if(this.max_length) {
43372             input.maxlength = this.max_length; 
43373         }
43374         
43375         if (this.name) {
43376             hiddenInput.name = this.name;
43377         }
43378
43379         if (this.disabled) {
43380             input.disabled = true;
43381         }
43382
43383         var clg = 12 - this.inputlg;
43384         var cmd = 12 - this.inputmd;
43385         var csm = 12 - this.inputsm;
43386         var cxs = 12 - this.inputxs;
43387         
43388         var container = {
43389             tag : 'div',
43390             cls : 'row roo-money-field',
43391             cn : [
43392                 {
43393                     tag : 'div',
43394                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43395                     cn : [
43396                         {
43397                             tag : 'div',
43398                             cls: 'roo-select2-container input-group',
43399                             cn: [
43400                                 {
43401                                     tag : 'input',
43402                                     cls : 'form-control roo-money-currency-input',
43403                                     autocomplete: 'new-password',
43404                                     readOnly : 1,
43405                                     name : this.currencyName
43406                                 },
43407                                 {
43408                                     tag :'span',
43409                                     cls : 'input-group-addon',
43410                                     cn : [
43411                                         {
43412                                             tag: 'span',
43413                                             cls: 'caret'
43414                                         }
43415                                     ]
43416                                 }
43417                             ]
43418                         }
43419                     ]
43420                 },
43421                 {
43422                     tag : 'div',
43423                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43424                     cn : [
43425                         {
43426                             tag: 'div',
43427                             cls: this.hasFeedback ? 'has-feedback' : '',
43428                             cn: [
43429                                 input
43430                             ]
43431                         }
43432                     ]
43433                 }
43434             ]
43435             
43436         };
43437         
43438         if (this.fieldLabel.length) {
43439             var indicator = {
43440                 tag: 'i',
43441                 tooltip: 'This field is required'
43442             };
43443
43444             var label = {
43445                 tag: 'label',
43446                 'for':  id,
43447                 cls: 'control-label',
43448                 cn: []
43449             };
43450
43451             var label_text = {
43452                 tag: 'span',
43453                 html: this.fieldLabel
43454             };
43455
43456             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43457             label.cn = [
43458                 indicator,
43459                 label_text
43460             ];
43461
43462             if(this.indicatorpos == 'right') {
43463                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43464                 label.cn = [
43465                     label_text,
43466                     indicator
43467                 ];
43468             }
43469
43470             if(align == 'left') {
43471                 container = {
43472                     tag: 'div',
43473                     cn: [
43474                         container
43475                     ]
43476                 };
43477
43478                 if(this.labelWidth > 12){
43479                     label.style = "width: " + this.labelWidth + 'px';
43480                 }
43481                 if(this.labelWidth < 13 && this.labelmd == 0){
43482                     this.labelmd = this.labelWidth;
43483                 }
43484                 if(this.labellg > 0){
43485                     label.cls += ' col-lg-' + this.labellg;
43486                     input.cls += ' col-lg-' + (12 - this.labellg);
43487                 }
43488                 if(this.labelmd > 0){
43489                     label.cls += ' col-md-' + this.labelmd;
43490                     container.cls += ' col-md-' + (12 - this.labelmd);
43491                 }
43492                 if(this.labelsm > 0){
43493                     label.cls += ' col-sm-' + this.labelsm;
43494                     container.cls += ' col-sm-' + (12 - this.labelsm);
43495                 }
43496                 if(this.labelxs > 0){
43497                     label.cls += ' col-xs-' + this.labelxs;
43498                     container.cls += ' col-xs-' + (12 - this.labelxs);
43499                 }
43500             }
43501         }
43502
43503         cfg.cn = [
43504             label,
43505             container,
43506             hiddenInput
43507         ];
43508         
43509         var settings = this;
43510
43511         ['xs','sm','md','lg'].map(function(size){
43512             if (settings[size]) {
43513                 cfg.cls += ' col-' + size + '-' + settings[size];
43514             }
43515         });
43516         
43517         return cfg;
43518     },
43519     
43520     initEvents : function()
43521     {
43522         this.indicator = this.indicatorEl();
43523         
43524         this.initCurrencyEvent();
43525         
43526         this.initNumberEvent();
43527     },
43528     
43529     initCurrencyEvent : function()
43530     {
43531         if (!this.store) {
43532             throw "can not find store for combo";
43533         }
43534         
43535         this.store = Roo.factory(this.store, Roo.data);
43536         this.store.parent = this;
43537         
43538         this.createList();
43539         
43540         this.triggerEl = this.el.select('.input-group-addon', true).first();
43541         
43542         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43543         
43544         var _this = this;
43545         
43546         (function(){
43547             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43548             _this.list.setWidth(lw);
43549         }).defer(100);
43550         
43551         this.list.on('mouseover', this.onViewOver, this);
43552         this.list.on('mousemove', this.onViewMove, this);
43553         this.list.on('scroll', this.onViewScroll, this);
43554         
43555         if(!this.tpl){
43556             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43557         }
43558         
43559         this.view = new Roo.View(this.list, this.tpl, {
43560             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43561         });
43562         
43563         this.view.on('click', this.onViewClick, this);
43564         
43565         this.store.on('beforeload', this.onBeforeLoad, this);
43566         this.store.on('load', this.onLoad, this);
43567         this.store.on('loadexception', this.onLoadException, this);
43568         
43569         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43570             "up" : function(e){
43571                 this.inKeyMode = true;
43572                 this.selectPrev();
43573             },
43574
43575             "down" : function(e){
43576                 if(!this.isExpanded()){
43577                     this.onTriggerClick();
43578                 }else{
43579                     this.inKeyMode = true;
43580                     this.selectNext();
43581                 }
43582             },
43583
43584             "enter" : function(e){
43585                 this.collapse();
43586                 
43587                 if(this.fireEvent("specialkey", this, e)){
43588                     this.onViewClick(false);
43589                 }
43590                 
43591                 return true;
43592             },
43593
43594             "esc" : function(e){
43595                 this.collapse();
43596             },
43597
43598             "tab" : function(e){
43599                 this.collapse();
43600                 
43601                 if(this.fireEvent("specialkey", this, e)){
43602                     this.onViewClick(false);
43603                 }
43604                 
43605                 return true;
43606             },
43607
43608             scope : this,
43609
43610             doRelay : function(foo, bar, hname){
43611                 if(hname == 'down' || this.scope.isExpanded()){
43612                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43613                 }
43614                 return true;
43615             },
43616
43617             forceKeyDown: true
43618         });
43619         
43620         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43621         
43622     },
43623     
43624     initNumberEvent : function(e)
43625     {
43626         this.inputEl().on("keydown" , this.fireKey,  this);
43627         this.inputEl().on("focus", this.onFocus,  this);
43628         this.inputEl().on("blur", this.onBlur,  this);
43629         
43630         this.inputEl().relayEvent('keyup', this);
43631         
43632         if(this.indicator){
43633             this.indicator.addClass('invisible');
43634         }
43635  
43636         this.originalValue = this.getValue();
43637         
43638         if(this.validationEvent == 'keyup'){
43639             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43640             this.inputEl().on('keyup', this.filterValidation, this);
43641         }
43642         else if(this.validationEvent !== false){
43643             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43644         }
43645         
43646         if(this.selectOnFocus){
43647             this.on("focus", this.preFocus, this);
43648             
43649         }
43650         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43651             this.inputEl().on("keypress", this.filterKeys, this);
43652         } else {
43653             this.inputEl().relayEvent('keypress', this);
43654         }
43655         
43656         var allowed = "0123456789";
43657         
43658         if(this.allowDecimals){
43659             allowed += this.decimalSeparator;
43660         }
43661         
43662         if(this.allowNegative){
43663             allowed += "-";
43664         }
43665         
43666         if(this.thousandsDelimiter) {
43667             allowed += ",";
43668         }
43669         
43670         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43671         
43672         var keyPress = function(e){
43673             
43674             var k = e.getKey();
43675             
43676             var c = e.getCharCode();
43677             
43678             if(
43679                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43680                     allowed.indexOf(String.fromCharCode(c)) === -1
43681             ){
43682                 e.stopEvent();
43683                 return;
43684             }
43685             
43686             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43687                 return;
43688             }
43689             
43690             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43691                 e.stopEvent();
43692             }
43693         };
43694         
43695         this.inputEl().on("keypress", keyPress, this);
43696         
43697     },
43698     
43699     onTriggerClick : function(e)
43700     {   
43701         if(this.disabled){
43702             return;
43703         }
43704         
43705         this.page = 0;
43706         this.loadNext = false;
43707         
43708         if(this.isExpanded()){
43709             this.collapse();
43710             return;
43711         }
43712         
43713         this.hasFocus = true;
43714         
43715         if(this.triggerAction == 'all') {
43716             this.doQuery(this.allQuery, true);
43717             return;
43718         }
43719         
43720         this.doQuery(this.getRawValue());
43721     },
43722     
43723     getCurrency : function()
43724     {   
43725         var v = this.currencyEl().getValue();
43726         
43727         return v;
43728     },
43729     
43730     restrictHeight : function()
43731     {
43732         this.list.alignTo(this.currencyEl(), this.listAlign);
43733         this.list.alignTo(this.currencyEl(), this.listAlign);
43734     },
43735     
43736     onViewClick : function(view, doFocus, el, e)
43737     {
43738         var index = this.view.getSelectedIndexes()[0];
43739         
43740         var r = this.store.getAt(index);
43741         
43742         if(r){
43743             this.onSelect(r, index);
43744         }
43745     },
43746     
43747     onSelect : function(record, index){
43748         
43749         if(this.fireEvent('beforeselect', this, record, index) !== false){
43750         
43751             this.setFromCurrencyData(index > -1 ? record.data : false);
43752             
43753             this.collapse();
43754             
43755             this.fireEvent('select', this, record, index);
43756         }
43757     },
43758     
43759     setFromCurrencyData : function(o)
43760     {
43761         var currency = '';
43762         
43763         this.lastCurrency = o;
43764         
43765         if (this.currencyField) {
43766             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43767         } else {
43768             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43769         }
43770         
43771         this.lastSelectionText = currency;
43772         
43773         //setting default currency
43774         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43775             this.setCurrency(this.defaultCurrency);
43776             return;
43777         }
43778         
43779         this.setCurrency(currency);
43780     },
43781     
43782     setFromData : function(o)
43783     {
43784         var c = {};
43785         
43786         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43787         
43788         this.setFromCurrencyData(c);
43789         
43790         var value = '';
43791         
43792         if (this.name) {
43793             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43794         } else {
43795             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43796         }
43797         
43798         this.setValue(value);
43799         
43800     },
43801     
43802     setCurrency : function(v)
43803     {   
43804         this.currencyValue = v;
43805         
43806         if(this.rendered){
43807             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43808             this.validate();
43809         }
43810     },
43811     
43812     setValue : function(v)
43813     {
43814         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43815         
43816         this.value = v;
43817         
43818         if(this.rendered){
43819             
43820             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43821             
43822             this.inputEl().dom.value = (v == '') ? '' :
43823                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43824             
43825             if(!this.allowZero && v === '0') {
43826                 this.hiddenEl().dom.value = '';
43827                 this.inputEl().dom.value = '';
43828             }
43829             
43830             this.validate();
43831         }
43832     },
43833     
43834     getRawValue : function()
43835     {
43836         var v = this.inputEl().getValue();
43837         
43838         return v;
43839     },
43840     
43841     getValue : function()
43842     {
43843         return this.fixPrecision(this.parseValue(this.getRawValue()));
43844     },
43845     
43846     parseValue : function(value)
43847     {
43848         if(this.thousandsDelimiter) {
43849             value += "";
43850             r = new RegExp(",", "g");
43851             value = value.replace(r, "");
43852         }
43853         
43854         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43855         return isNaN(value) ? '' : value;
43856         
43857     },
43858     
43859     fixPrecision : function(value)
43860     {
43861         if(this.thousandsDelimiter) {
43862             value += "";
43863             r = new RegExp(",", "g");
43864             value = value.replace(r, "");
43865         }
43866         
43867         var nan = isNaN(value);
43868         
43869         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43870             return nan ? '' : value;
43871         }
43872         return parseFloat(value).toFixed(this.decimalPrecision);
43873     },
43874     
43875     decimalPrecisionFcn : function(v)
43876     {
43877         return Math.floor(v);
43878     },
43879     
43880     validateValue : function(value)
43881     {
43882         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43883             return false;
43884         }
43885         
43886         var num = this.parseValue(value);
43887         
43888         if(isNaN(num)){
43889             this.markInvalid(String.format(this.nanText, value));
43890             return false;
43891         }
43892         
43893         if(num < this.minValue){
43894             this.markInvalid(String.format(this.minText, this.minValue));
43895             return false;
43896         }
43897         
43898         if(num > this.maxValue){
43899             this.markInvalid(String.format(this.maxText, this.maxValue));
43900             return false;
43901         }
43902         
43903         return true;
43904     },
43905     
43906     validate : function()
43907     {
43908         if(this.disabled || this.allowBlank){
43909             this.markValid();
43910             return true;
43911         }
43912         
43913         var currency = this.getCurrency();
43914         
43915         if(this.validateValue(this.getRawValue()) && currency.length){
43916             this.markValid();
43917             return true;
43918         }
43919         
43920         this.markInvalid();
43921         return false;
43922     },
43923     
43924     getName: function()
43925     {
43926         return this.name;
43927     },
43928     
43929     beforeBlur : function()
43930     {
43931         if(!this.castInt){
43932             return;
43933         }
43934         
43935         var v = this.parseValue(this.getRawValue());
43936         
43937         if(v || v == 0){
43938             this.setValue(v);
43939         }
43940     },
43941     
43942     onBlur : function()
43943     {
43944         this.beforeBlur();
43945         
43946         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43947             //this.el.removeClass(this.focusClass);
43948         }
43949         
43950         this.hasFocus = false;
43951         
43952         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43953             this.validate();
43954         }
43955         
43956         var v = this.getValue();
43957         
43958         if(String(v) !== String(this.startValue)){
43959             this.fireEvent('change', this, v, this.startValue);
43960         }
43961         
43962         this.fireEvent("blur", this);
43963     },
43964     
43965     inputEl : function()
43966     {
43967         return this.el.select('.roo-money-amount-input', true).first();
43968     },
43969     
43970     currencyEl : function()
43971     {
43972         return this.el.select('.roo-money-currency-input', true).first();
43973     },
43974     
43975     hiddenEl : function()
43976     {
43977         return this.el.select('input.hidden-number-input',true).first();
43978     }
43979     
43980 });/**
43981  * @class Roo.bootstrap.BezierSignature
43982  * @extends Roo.bootstrap.Component
43983  * Bootstrap BezierSignature class
43984  * This script refer to:
43985  *    Title: Signature Pad
43986  *    Author: szimek
43987  *    Availability: https://github.com/szimek/signature_pad
43988  *
43989  * @constructor
43990  * Create a new BezierSignature
43991  * @param {Object} config The config object
43992  */
43993
43994 Roo.bootstrap.BezierSignature = function(config){
43995     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43996     this.addEvents({
43997         "resize" : true
43998     });
43999 };
44000
44001 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
44002 {
44003      
44004     curve_data: [],
44005     
44006     is_empty: true,
44007     
44008     mouse_btn_down: true,
44009     
44010     /**
44011      * @cfg {int} canvas height
44012      */
44013     canvas_height: '200px',
44014     
44015     /**
44016      * @cfg {float|function} Radius of a single dot.
44017      */ 
44018     dot_size: false,
44019     
44020     /**
44021      * @cfg {float} Minimum width of a line. Defaults to 0.5.
44022      */
44023     min_width: 0.5,
44024     
44025     /**
44026      * @cfg {float} Maximum width of a line. Defaults to 2.5.
44027      */
44028     max_width: 2.5,
44029     
44030     /**
44031      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
44032      */
44033     throttle: 16,
44034     
44035     /**
44036      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
44037      */
44038     min_distance: 5,
44039     
44040     /**
44041      * @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.
44042      */
44043     bg_color: 'rgba(0, 0, 0, 0)',
44044     
44045     /**
44046      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
44047      */
44048     dot_color: 'black',
44049     
44050     /**
44051      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44052      */ 
44053     velocity_filter_weight: 0.7,
44054     
44055     /**
44056      * @cfg {function} Callback when stroke begin. 
44057      */
44058     onBegin: false,
44059     
44060     /**
44061      * @cfg {function} Callback when stroke end.
44062      */
44063     onEnd: false,
44064     
44065     getAutoCreate : function()
44066     {
44067         var cls = 'roo-signature column';
44068         
44069         if(this.cls){
44070             cls += ' ' + this.cls;
44071         }
44072         
44073         var col_sizes = [
44074             'lg',
44075             'md',
44076             'sm',
44077             'xs'
44078         ];
44079         
44080         for(var i = 0; i < col_sizes.length; i++) {
44081             if(this[col_sizes[i]]) {
44082                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44083             }
44084         }
44085         
44086         var cfg = {
44087             tag: 'div',
44088             cls: cls,
44089             cn: [
44090                 {
44091                     tag: 'div',
44092                     cls: 'roo-signature-body',
44093                     cn: [
44094                         {
44095                             tag: 'canvas',
44096                             cls: 'roo-signature-body-canvas',
44097                             height: this.canvas_height,
44098                             width: this.canvas_width
44099                         }
44100                     ]
44101                 },
44102                 {
44103                     tag: 'input',
44104                     type: 'file',
44105                     style: 'display: none'
44106                 }
44107             ]
44108         };
44109         
44110         return cfg;
44111     },
44112     
44113     initEvents: function() 
44114     {
44115         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44116         
44117         var canvas = this.canvasEl();
44118         
44119         // mouse && touch event swapping...
44120         canvas.dom.style.touchAction = 'none';
44121         canvas.dom.style.msTouchAction = 'none';
44122         
44123         this.mouse_btn_down = false;
44124         canvas.on('mousedown', this._handleMouseDown, this);
44125         canvas.on('mousemove', this._handleMouseMove, this);
44126         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44127         
44128         if (window.PointerEvent) {
44129             canvas.on('pointerdown', this._handleMouseDown, this);
44130             canvas.on('pointermove', this._handleMouseMove, this);
44131             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44132         }
44133         
44134         if ('ontouchstart' in window) {
44135             canvas.on('touchstart', this._handleTouchStart, this);
44136             canvas.on('touchmove', this._handleTouchMove, this);
44137             canvas.on('touchend', this._handleTouchEnd, this);
44138         }
44139         
44140         Roo.EventManager.onWindowResize(this.resize, this, true);
44141         
44142         // file input event
44143         this.fileEl().on('change', this.uploadImage, this);
44144         
44145         this.clear();
44146         
44147         this.resize();
44148     },
44149     
44150     resize: function(){
44151         
44152         var canvas = this.canvasEl().dom;
44153         var ctx = this.canvasElCtx();
44154         var img_data = false;
44155         
44156         if(canvas.width > 0) {
44157             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44158         }
44159         // setting canvas width will clean img data
44160         canvas.width = 0;
44161         
44162         var style = window.getComputedStyle ? 
44163             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44164             
44165         var padding_left = parseInt(style.paddingLeft) || 0;
44166         var padding_right = parseInt(style.paddingRight) || 0;
44167         
44168         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44169         
44170         if(img_data) {
44171             ctx.putImageData(img_data, 0, 0);
44172         }
44173     },
44174     
44175     _handleMouseDown: function(e)
44176     {
44177         if (e.browserEvent.which === 1) {
44178             this.mouse_btn_down = true;
44179             this.strokeBegin(e);
44180         }
44181     },
44182     
44183     _handleMouseMove: function (e)
44184     {
44185         if (this.mouse_btn_down) {
44186             this.strokeMoveUpdate(e);
44187         }
44188     },
44189     
44190     _handleMouseUp: function (e)
44191     {
44192         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44193             this.mouse_btn_down = false;
44194             this.strokeEnd(e);
44195         }
44196     },
44197     
44198     _handleTouchStart: function (e) {
44199         
44200         e.preventDefault();
44201         if (e.browserEvent.targetTouches.length === 1) {
44202             // var touch = e.browserEvent.changedTouches[0];
44203             // this.strokeBegin(touch);
44204             
44205              this.strokeBegin(e); // assume e catching the correct xy...
44206         }
44207     },
44208     
44209     _handleTouchMove: function (e) {
44210         e.preventDefault();
44211         // var touch = event.targetTouches[0];
44212         // _this._strokeMoveUpdate(touch);
44213         this.strokeMoveUpdate(e);
44214     },
44215     
44216     _handleTouchEnd: function (e) {
44217         var wasCanvasTouched = e.target === this.canvasEl().dom;
44218         if (wasCanvasTouched) {
44219             e.preventDefault();
44220             // var touch = event.changedTouches[0];
44221             // _this._strokeEnd(touch);
44222             this.strokeEnd(e);
44223         }
44224     },
44225     
44226     reset: function () {
44227         this._lastPoints = [];
44228         this._lastVelocity = 0;
44229         this._lastWidth = (this.min_width + this.max_width) / 2;
44230         this.canvasElCtx().fillStyle = this.dot_color;
44231     },
44232     
44233     strokeMoveUpdate: function(e)
44234     {
44235         this.strokeUpdate(e);
44236         
44237         if (this.throttle) {
44238             this.throttleStroke(this.strokeUpdate, this.throttle);
44239         }
44240         else {
44241             this.strokeUpdate(e);
44242         }
44243     },
44244     
44245     strokeBegin: function(e)
44246     {
44247         var newPointGroup = {
44248             color: this.dot_color,
44249             points: []
44250         };
44251         
44252         if (typeof this.onBegin === 'function') {
44253             this.onBegin(e);
44254         }
44255         
44256         this.curve_data.push(newPointGroup);
44257         this.reset();
44258         this.strokeUpdate(e);
44259     },
44260     
44261     strokeUpdate: function(e)
44262     {
44263         var rect = this.canvasEl().dom.getBoundingClientRect();
44264         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44265         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44266         var lastPoints = lastPointGroup.points;
44267         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44268         var isLastPointTooClose = lastPoint
44269             ? point.distanceTo(lastPoint) <= this.min_distance
44270             : false;
44271         var color = lastPointGroup.color;
44272         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44273             var curve = this.addPoint(point);
44274             if (!lastPoint) {
44275                 this.drawDot({color: color, point: point});
44276             }
44277             else if (curve) {
44278                 this.drawCurve({color: color, curve: curve});
44279             }
44280             lastPoints.push({
44281                 time: point.time,
44282                 x: point.x,
44283                 y: point.y
44284             });
44285         }
44286     },
44287     
44288     strokeEnd: function(e)
44289     {
44290         this.strokeUpdate(e);
44291         if (typeof this.onEnd === 'function') {
44292             this.onEnd(e);
44293         }
44294     },
44295     
44296     addPoint:  function (point) {
44297         var _lastPoints = this._lastPoints;
44298         _lastPoints.push(point);
44299         if (_lastPoints.length > 2) {
44300             if (_lastPoints.length === 3) {
44301                 _lastPoints.unshift(_lastPoints[0]);
44302             }
44303             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44304             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44305             _lastPoints.shift();
44306             return curve;
44307         }
44308         return null;
44309     },
44310     
44311     calculateCurveWidths: function (startPoint, endPoint) {
44312         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44313             (1 - this.velocity_filter_weight) * this._lastVelocity;
44314
44315         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44316         var widths = {
44317             end: newWidth,
44318             start: this._lastWidth
44319         };
44320         
44321         this._lastVelocity = velocity;
44322         this._lastWidth = newWidth;
44323         return widths;
44324     },
44325     
44326     drawDot: function (_a) {
44327         var color = _a.color, point = _a.point;
44328         var ctx = this.canvasElCtx();
44329         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44330         ctx.beginPath();
44331         this.drawCurveSegment(point.x, point.y, width);
44332         ctx.closePath();
44333         ctx.fillStyle = color;
44334         ctx.fill();
44335     },
44336     
44337     drawCurve: function (_a) {
44338         var color = _a.color, curve = _a.curve;
44339         var ctx = this.canvasElCtx();
44340         var widthDelta = curve.endWidth - curve.startWidth;
44341         var drawSteps = Math.floor(curve.length()) * 2;
44342         ctx.beginPath();
44343         ctx.fillStyle = color;
44344         for (var i = 0; i < drawSteps; i += 1) {
44345         var t = i / drawSteps;
44346         var tt = t * t;
44347         var ttt = tt * t;
44348         var u = 1 - t;
44349         var uu = u * u;
44350         var uuu = uu * u;
44351         var x = uuu * curve.startPoint.x;
44352         x += 3 * uu * t * curve.control1.x;
44353         x += 3 * u * tt * curve.control2.x;
44354         x += ttt * curve.endPoint.x;
44355         var y = uuu * curve.startPoint.y;
44356         y += 3 * uu * t * curve.control1.y;
44357         y += 3 * u * tt * curve.control2.y;
44358         y += ttt * curve.endPoint.y;
44359         var width = curve.startWidth + ttt * widthDelta;
44360         this.drawCurveSegment(x, y, width);
44361         }
44362         ctx.closePath();
44363         ctx.fill();
44364     },
44365     
44366     drawCurveSegment: function (x, y, width) {
44367         var ctx = this.canvasElCtx();
44368         ctx.moveTo(x, y);
44369         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44370         this.is_empty = false;
44371     },
44372     
44373     clear: function()
44374     {
44375         var ctx = this.canvasElCtx();
44376         var canvas = this.canvasEl().dom;
44377         ctx.fillStyle = this.bg_color;
44378         ctx.clearRect(0, 0, canvas.width, canvas.height);
44379         ctx.fillRect(0, 0, canvas.width, canvas.height);
44380         this.curve_data = [];
44381         this.reset();
44382         this.is_empty = true;
44383     },
44384     
44385     fileEl: function()
44386     {
44387         return  this.el.select('input',true).first();
44388     },
44389     
44390     canvasEl: function()
44391     {
44392         return this.el.select('canvas',true).first();
44393     },
44394     
44395     canvasElCtx: function()
44396     {
44397         return this.el.select('canvas',true).first().dom.getContext('2d');
44398     },
44399     
44400     getImage: function(type)
44401     {
44402         if(this.is_empty) {
44403             return false;
44404         }
44405         
44406         // encryption ?
44407         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44408     },
44409     
44410     drawFromImage: function(img_src)
44411     {
44412         var img = new Image();
44413         
44414         img.onload = function(){
44415             this.canvasElCtx().drawImage(img, 0, 0);
44416         }.bind(this);
44417         
44418         img.src = img_src;
44419         
44420         this.is_empty = false;
44421     },
44422     
44423     selectImage: function()
44424     {
44425         this.fileEl().dom.click();
44426     },
44427     
44428     uploadImage: function(e)
44429     {
44430         var reader = new FileReader();
44431         
44432         reader.onload = function(e){
44433             var img = new Image();
44434             img.onload = function(){
44435                 this.reset();
44436                 this.canvasElCtx().drawImage(img, 0, 0);
44437             }.bind(this);
44438             img.src = e.target.result;
44439         }.bind(this);
44440         
44441         reader.readAsDataURL(e.target.files[0]);
44442     },
44443     
44444     // Bezier Point Constructor
44445     Point: (function () {
44446         function Point(x, y, time) {
44447             this.x = x;
44448             this.y = y;
44449             this.time = time || Date.now();
44450         }
44451         Point.prototype.distanceTo = function (start) {
44452             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44453         };
44454         Point.prototype.equals = function (other) {
44455             return this.x === other.x && this.y === other.y && this.time === other.time;
44456         };
44457         Point.prototype.velocityFrom = function (start) {
44458             return this.time !== start.time
44459             ? this.distanceTo(start) / (this.time - start.time)
44460             : 0;
44461         };
44462         return Point;
44463     }()),
44464     
44465     
44466     // Bezier Constructor
44467     Bezier: (function () {
44468         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44469             this.startPoint = startPoint;
44470             this.control2 = control2;
44471             this.control1 = control1;
44472             this.endPoint = endPoint;
44473             this.startWidth = startWidth;
44474             this.endWidth = endWidth;
44475         }
44476         Bezier.fromPoints = function (points, widths, scope) {
44477             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44478             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44479             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44480         };
44481         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44482             var dx1 = s1.x - s2.x;
44483             var dy1 = s1.y - s2.y;
44484             var dx2 = s2.x - s3.x;
44485             var dy2 = s2.y - s3.y;
44486             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44487             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44488             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44489             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44490             var dxm = m1.x - m2.x;
44491             var dym = m1.y - m2.y;
44492             var k = l2 / (l1 + l2);
44493             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44494             var tx = s2.x - cm.x;
44495             var ty = s2.y - cm.y;
44496             return {
44497                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44498                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44499             };
44500         };
44501         Bezier.prototype.length = function () {
44502             var steps = 10;
44503             var length = 0;
44504             var px;
44505             var py;
44506             for (var i = 0; i <= steps; i += 1) {
44507                 var t = i / steps;
44508                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44509                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44510                 if (i > 0) {
44511                     var xdiff = cx - px;
44512                     var ydiff = cy - py;
44513                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44514                 }
44515                 px = cx;
44516                 py = cy;
44517             }
44518             return length;
44519         };
44520         Bezier.prototype.point = function (t, start, c1, c2, end) {
44521             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44522             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44523             + (3.0 * c2 * (1.0 - t) * t * t)
44524             + (end * t * t * t);
44525         };
44526         return Bezier;
44527     }()),
44528     
44529     throttleStroke: function(fn, wait) {
44530       if (wait === void 0) { wait = 250; }
44531       var previous = 0;
44532       var timeout = null;
44533       var result;
44534       var storedContext;
44535       var storedArgs;
44536       var later = function () {
44537           previous = Date.now();
44538           timeout = null;
44539           result = fn.apply(storedContext, storedArgs);
44540           if (!timeout) {
44541               storedContext = null;
44542               storedArgs = [];
44543           }
44544       };
44545       return function wrapper() {
44546           var args = [];
44547           for (var _i = 0; _i < arguments.length; _i++) {
44548               args[_i] = arguments[_i];
44549           }
44550           var now = Date.now();
44551           var remaining = wait - (now - previous);
44552           storedContext = this;
44553           storedArgs = args;
44554           if (remaining <= 0 || remaining > wait) {
44555               if (timeout) {
44556                   clearTimeout(timeout);
44557                   timeout = null;
44558               }
44559               previous = now;
44560               result = fn.apply(storedContext, storedArgs);
44561               if (!timeout) {
44562                   storedContext = null;
44563                   storedArgs = [];
44564               }
44565           }
44566           else if (!timeout) {
44567               timeout = window.setTimeout(later, remaining);
44568           }
44569           return result;
44570       };
44571   }
44572   
44573 });
44574
44575  
44576
44577