Roo/bootstrap/CardUploader.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     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.header = html;
2683         if (this.headerContainerEl) {
2684             this.headerContainerEl.dom.innerHTML = html;
2685         }
2686     }
2687
2688     
2689 });
2690
2691 /*
2692  * - LGPL
2693  *
2694  * Card header - holder for the card header elements.
2695  * 
2696  */
2697
2698 /**
2699  * @class Roo.bootstrap.CardHeader
2700  * @extends Roo.bootstrap.Element
2701  * Bootstrap CardHeader class
2702  * @constructor
2703  * Create a new Card Header - that you can embed children into
2704  * @param {Object} config The config object
2705  */
2706
2707 Roo.bootstrap.CardHeader = function(config){
2708     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2709 };
2710
2711 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2712     
2713     
2714     container_method : 'getCardHeader' 
2715     
2716      
2717     
2718     
2719    
2720 });
2721
2722  
2723
2724  /*
2725  * - LGPL
2726  *
2727  * Card footer - holder for the card footer elements.
2728  * 
2729  */
2730
2731 /**
2732  * @class Roo.bootstrap.CardFooter
2733  * @extends Roo.bootstrap.Element
2734  * Bootstrap CardFooter class
2735  * @constructor
2736  * Create a new Card Footer - that you can embed children into
2737  * @param {Object} config The config object
2738  */
2739
2740 Roo.bootstrap.CardFooter = function(config){
2741     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2742 };
2743
2744 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2745     
2746     
2747     container_method : 'getCardFooter' 
2748     
2749      
2750     
2751     
2752    
2753 });
2754
2755  
2756
2757  /*
2758  * - LGPL
2759  *
2760  * Card header - holder for the card header elements.
2761  * 
2762  */
2763
2764 /**
2765  * @class Roo.bootstrap.CardImageTop
2766  * @extends Roo.bootstrap.Element
2767  * Bootstrap CardImageTop class
2768  * @constructor
2769  * Create a new Card Image Top container
2770  * @param {Object} config The config object
2771  */
2772
2773 Roo.bootstrap.CardImageTop = function(config){
2774     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2775 };
2776
2777 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2778     
2779    
2780     container_method : 'getCardImageTop' 
2781     
2782      
2783     
2784    
2785 });
2786
2787  
2788
2789  /*
2790  * - LGPL
2791  *
2792  * image
2793  * 
2794  */
2795
2796
2797 /**
2798  * @class Roo.bootstrap.Img
2799  * @extends Roo.bootstrap.Component
2800  * Bootstrap Img class
2801  * @cfg {Boolean} imgResponsive false | true
2802  * @cfg {String} border rounded | circle | thumbnail
2803  * @cfg {String} src image source
2804  * @cfg {String} alt image alternative text
2805  * @cfg {String} href a tag href
2806  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2807  * @cfg {String} xsUrl xs image source
2808  * @cfg {String} smUrl sm image source
2809  * @cfg {String} mdUrl md image source
2810  * @cfg {String} lgUrl lg image source
2811  * 
2812  * @constructor
2813  * Create a new Input
2814  * @param {Object} config The config object
2815  */
2816
2817 Roo.bootstrap.Img = function(config){
2818     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2819     
2820     this.addEvents({
2821         // img events
2822         /**
2823          * @event click
2824          * The img click event for the img.
2825          * @param {Roo.EventObject} e
2826          */
2827         "click" : true
2828     });
2829 };
2830
2831 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2832     
2833     imgResponsive: true,
2834     border: '',
2835     src: 'about:blank',
2836     href: false,
2837     target: false,
2838     xsUrl: '',
2839     smUrl: '',
2840     mdUrl: '',
2841     lgUrl: '',
2842
2843     getAutoCreate : function()
2844     {   
2845         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2846             return this.createSingleImg();
2847         }
2848         
2849         var cfg = {
2850             tag: 'div',
2851             cls: 'roo-image-responsive-group',
2852             cn: []
2853         };
2854         var _this = this;
2855         
2856         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2857             
2858             if(!_this[size + 'Url']){
2859                 return;
2860             }
2861             
2862             var img = {
2863                 tag: 'img',
2864                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2865                 html: _this.html || cfg.html,
2866                 src: _this[size + 'Url']
2867             };
2868             
2869             img.cls += ' roo-image-responsive-' + size;
2870             
2871             var s = ['xs', 'sm', 'md', 'lg'];
2872             
2873             s.splice(s.indexOf(size), 1);
2874             
2875             Roo.each(s, function(ss){
2876                 img.cls += ' hidden-' + ss;
2877             });
2878             
2879             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2880                 cfg.cls += ' img-' + _this.border;
2881             }
2882             
2883             if(_this.alt){
2884                 cfg.alt = _this.alt;
2885             }
2886             
2887             if(_this.href){
2888                 var a = {
2889                     tag: 'a',
2890                     href: _this.href,
2891                     cn: [
2892                         img
2893                     ]
2894                 };
2895
2896                 if(this.target){
2897                     a.target = _this.target;
2898                 }
2899             }
2900             
2901             cfg.cn.push((_this.href) ? a : img);
2902             
2903         });
2904         
2905         return cfg;
2906     },
2907     
2908     createSingleImg : function()
2909     {
2910         var cfg = {
2911             tag: 'img',
2912             cls: (this.imgResponsive) ? 'img-responsive' : '',
2913             html : null,
2914             src : 'about:blank'  // just incase src get's set to undefined?!?
2915         };
2916         
2917         cfg.html = this.html || cfg.html;
2918         
2919         cfg.src = this.src || cfg.src;
2920         
2921         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2922             cfg.cls += ' img-' + this.border;
2923         }
2924         
2925         if(this.alt){
2926             cfg.alt = this.alt;
2927         }
2928         
2929         if(this.href){
2930             var a = {
2931                 tag: 'a',
2932                 href: this.href,
2933                 cn: [
2934                     cfg
2935                 ]
2936             };
2937             
2938             if(this.target){
2939                 a.target = this.target;
2940             }
2941             
2942         }
2943         
2944         return (this.href) ? a : cfg;
2945     },
2946     
2947     initEvents: function() 
2948     {
2949         if(!this.href){
2950             this.el.on('click', this.onClick, this);
2951         }
2952         
2953     },
2954     
2955     onClick : function(e)
2956     {
2957         Roo.log('img onclick');
2958         this.fireEvent('click', this, e);
2959     },
2960     /**
2961      * Sets the url of the image - used to update it
2962      * @param {String} url the url of the image
2963      */
2964     
2965     setSrc : function(url)
2966     {
2967         this.src =  url;
2968         
2969         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2970             this.el.dom.src =  url;
2971             return;
2972         }
2973         
2974         this.el.select('img', true).first().dom.src =  url;
2975     }
2976     
2977     
2978    
2979 });
2980
2981  /*
2982  * - LGPL
2983  *
2984  * image
2985  * 
2986  */
2987
2988
2989 /**
2990  * @class Roo.bootstrap.Link
2991  * @extends Roo.bootstrap.Component
2992  * Bootstrap Link Class
2993  * @cfg {String} alt image alternative text
2994  * @cfg {String} href a tag href
2995  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2996  * @cfg {String} html the content of the link.
2997  * @cfg {String} anchor name for the anchor link
2998  * @cfg {String} fa - favicon
2999
3000  * @cfg {Boolean} preventDefault (true | false) default false
3001
3002  * 
3003  * @constructor
3004  * Create a new Input
3005  * @param {Object} config The config object
3006  */
3007
3008 Roo.bootstrap.Link = function(config){
3009     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3010     
3011     this.addEvents({
3012         // img events
3013         /**
3014          * @event click
3015          * The img click event for the img.
3016          * @param {Roo.EventObject} e
3017          */
3018         "click" : true
3019     });
3020 };
3021
3022 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3023     
3024     href: false,
3025     target: false,
3026     preventDefault: false,
3027     anchor : false,
3028     alt : false,
3029     fa: false,
3030
3031
3032     getAutoCreate : function()
3033     {
3034         var html = this.html || '';
3035         
3036         if (this.fa !== false) {
3037             html = '<i class="fa fa-' + this.fa + '"></i>';
3038         }
3039         var cfg = {
3040             tag: 'a'
3041         };
3042         // anchor's do not require html/href...
3043         if (this.anchor === false) {
3044             cfg.html = html;
3045             cfg.href = this.href || '#';
3046         } else {
3047             cfg.name = this.anchor;
3048             if (this.html !== false || this.fa !== false) {
3049                 cfg.html = html;
3050             }
3051             if (this.href !== false) {
3052                 cfg.href = this.href;
3053             }
3054         }
3055         
3056         if(this.alt !== false){
3057             cfg.alt = this.alt;
3058         }
3059         
3060         
3061         if(this.target !== false) {
3062             cfg.target = this.target;
3063         }
3064         
3065         return cfg;
3066     },
3067     
3068     initEvents: function() {
3069         
3070         if(!this.href || this.preventDefault){
3071             this.el.on('click', this.onClick, this);
3072         }
3073     },
3074     
3075     onClick : function(e)
3076     {
3077         if(this.preventDefault){
3078             e.preventDefault();
3079         }
3080         //Roo.log('img onclick');
3081         this.fireEvent('click', this, e);
3082     }
3083    
3084 });
3085
3086  /*
3087  * - LGPL
3088  *
3089  * header
3090  * 
3091  */
3092
3093 /**
3094  * @class Roo.bootstrap.Header
3095  * @extends Roo.bootstrap.Component
3096  * Bootstrap Header class
3097  * @cfg {String} html content of header
3098  * @cfg {Number} level (1|2|3|4|5|6) default 1
3099  * 
3100  * @constructor
3101  * Create a new Header
3102  * @param {Object} config The config object
3103  */
3104
3105
3106 Roo.bootstrap.Header  = function(config){
3107     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3108 };
3109
3110 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3111     
3112     //href : false,
3113     html : false,
3114     level : 1,
3115     
3116     
3117     
3118     getAutoCreate : function(){
3119         
3120         
3121         
3122         var cfg = {
3123             tag: 'h' + (1 *this.level),
3124             html: this.html || ''
3125         } ;
3126         
3127         return cfg;
3128     }
3129    
3130 });
3131
3132  
3133
3134  /*
3135  * Based on:
3136  * Ext JS Library 1.1.1
3137  * Copyright(c) 2006-2007, Ext JS, LLC.
3138  *
3139  * Originally Released Under LGPL - original licence link has changed is not relivant.
3140  *
3141  * Fork - LGPL
3142  * <script type="text/javascript">
3143  */
3144  
3145 /**
3146  * @class Roo.bootstrap.MenuMgr
3147  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3148  * @singleton
3149  */
3150 Roo.bootstrap.MenuMgr = function(){
3151    var menus, active, groups = {}, attached = false, lastShow = new Date();
3152
3153    // private - called when first menu is created
3154    function init(){
3155        menus = {};
3156        active = new Roo.util.MixedCollection();
3157        Roo.get(document).addKeyListener(27, function(){
3158            if(active.length > 0){
3159                hideAll();
3160            }
3161        });
3162    }
3163
3164    // private
3165    function hideAll(){
3166        if(active && active.length > 0){
3167            var c = active.clone();
3168            c.each(function(m){
3169                m.hide();
3170            });
3171        }
3172    }
3173
3174    // private
3175    function onHide(m){
3176        active.remove(m);
3177        if(active.length < 1){
3178            Roo.get(document).un("mouseup", onMouseDown);
3179             
3180            attached = false;
3181        }
3182    }
3183
3184    // private
3185    function onShow(m){
3186        var last = active.last();
3187        lastShow = new Date();
3188        active.add(m);
3189        if(!attached){
3190           Roo.get(document).on("mouseup", onMouseDown);
3191            
3192            attached = true;
3193        }
3194        if(m.parentMenu){
3195           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3196           m.parentMenu.activeChild = m;
3197        }else if(last && last.isVisible()){
3198           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3199        }
3200    }
3201
3202    // private
3203    function onBeforeHide(m){
3204        if(m.activeChild){
3205            m.activeChild.hide();
3206        }
3207        if(m.autoHideTimer){
3208            clearTimeout(m.autoHideTimer);
3209            delete m.autoHideTimer;
3210        }
3211    }
3212
3213    // private
3214    function onBeforeShow(m){
3215        var pm = m.parentMenu;
3216        if(!pm && !m.allowOtherMenus){
3217            hideAll();
3218        }else if(pm && pm.activeChild && active != m){
3219            pm.activeChild.hide();
3220        }
3221    }
3222
3223    // private this should really trigger on mouseup..
3224    function onMouseDown(e){
3225         Roo.log("on Mouse Up");
3226         
3227         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3228             Roo.log("MenuManager hideAll");
3229             hideAll();
3230             e.stopEvent();
3231         }
3232         
3233         
3234    }
3235
3236    // private
3237    function onBeforeCheck(mi, state){
3238        if(state){
3239            var g = groups[mi.group];
3240            for(var i = 0, l = g.length; i < l; i++){
3241                if(g[i] != mi){
3242                    g[i].setChecked(false);
3243                }
3244            }
3245        }
3246    }
3247
3248    return {
3249
3250        /**
3251         * Hides all menus that are currently visible
3252         */
3253        hideAll : function(){
3254             hideAll();  
3255        },
3256
3257        // private
3258        register : function(menu){
3259            if(!menus){
3260                init();
3261            }
3262            menus[menu.id] = menu;
3263            menu.on("beforehide", onBeforeHide);
3264            menu.on("hide", onHide);
3265            menu.on("beforeshow", onBeforeShow);
3266            menu.on("show", onShow);
3267            var g = menu.group;
3268            if(g && menu.events["checkchange"]){
3269                if(!groups[g]){
3270                    groups[g] = [];
3271                }
3272                groups[g].push(menu);
3273                menu.on("checkchange", onCheck);
3274            }
3275        },
3276
3277         /**
3278          * Returns a {@link Roo.menu.Menu} object
3279          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3280          * be used to generate and return a new Menu instance.
3281          */
3282        get : function(menu){
3283            if(typeof menu == "string"){ // menu id
3284                return menus[menu];
3285            }else if(menu.events){  // menu instance
3286                return menu;
3287            }
3288            /*else if(typeof menu.length == 'number'){ // array of menu items?
3289                return new Roo.bootstrap.Menu({items:menu});
3290            }else{ // otherwise, must be a config
3291                return new Roo.bootstrap.Menu(menu);
3292            }
3293            */
3294            return false;
3295        },
3296
3297        // private
3298        unregister : function(menu){
3299            delete menus[menu.id];
3300            menu.un("beforehide", onBeforeHide);
3301            menu.un("hide", onHide);
3302            menu.un("beforeshow", onBeforeShow);
3303            menu.un("show", onShow);
3304            var g = menu.group;
3305            if(g && menu.events["checkchange"]){
3306                groups[g].remove(menu);
3307                menu.un("checkchange", onCheck);
3308            }
3309        },
3310
3311        // private
3312        registerCheckable : function(menuItem){
3313            var g = menuItem.group;
3314            if(g){
3315                if(!groups[g]){
3316                    groups[g] = [];
3317                }
3318                groups[g].push(menuItem);
3319                menuItem.on("beforecheckchange", onBeforeCheck);
3320            }
3321        },
3322
3323        // private
3324        unregisterCheckable : function(menuItem){
3325            var g = menuItem.group;
3326            if(g){
3327                groups[g].remove(menuItem);
3328                menuItem.un("beforecheckchange", onBeforeCheck);
3329            }
3330        }
3331    };
3332 }();/*
3333  * - LGPL
3334  *
3335  * menu
3336  * 
3337  */
3338
3339 /**
3340  * @class Roo.bootstrap.Menu
3341  * @extends Roo.bootstrap.Component
3342  * Bootstrap Menu class - container for MenuItems
3343  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3344  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3345  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3346  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3347  * 
3348  * @constructor
3349  * Create a new Menu
3350  * @param {Object} config The config object
3351  */
3352
3353
3354 Roo.bootstrap.Menu = function(config){
3355     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3356     if (this.registerMenu && this.type != 'treeview')  {
3357         Roo.bootstrap.MenuMgr.register(this);
3358     }
3359     
3360     
3361     this.addEvents({
3362         /**
3363          * @event beforeshow
3364          * Fires before this menu is displayed (return false to block)
3365          * @param {Roo.menu.Menu} this
3366          */
3367         beforeshow : true,
3368         /**
3369          * @event beforehide
3370          * Fires before this menu is hidden (return false to block)
3371          * @param {Roo.menu.Menu} this
3372          */
3373         beforehide : true,
3374         /**
3375          * @event show
3376          * Fires after this menu is displayed
3377          * @param {Roo.menu.Menu} this
3378          */
3379         show : true,
3380         /**
3381          * @event hide
3382          * Fires after this menu is hidden
3383          * @param {Roo.menu.Menu} this
3384          */
3385         hide : true,
3386         /**
3387          * @event click
3388          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3389          * @param {Roo.menu.Menu} this
3390          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3391          * @param {Roo.EventObject} e
3392          */
3393         click : true,
3394         /**
3395          * @event mouseover
3396          * Fires when the mouse is hovering over this menu
3397          * @param {Roo.menu.Menu} this
3398          * @param {Roo.EventObject} e
3399          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3400          */
3401         mouseover : true,
3402         /**
3403          * @event mouseout
3404          * Fires when the mouse exits this menu
3405          * @param {Roo.menu.Menu} this
3406          * @param {Roo.EventObject} e
3407          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3408          */
3409         mouseout : true,
3410         /**
3411          * @event itemclick
3412          * Fires when a menu item contained in this menu is clicked
3413          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3414          * @param {Roo.EventObject} e
3415          */
3416         itemclick: true
3417     });
3418     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3419 };
3420
3421 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3422     
3423    /// html : false,
3424     //align : '',
3425     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3426     type: false,
3427     /**
3428      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3429      */
3430     registerMenu : true,
3431     
3432     menuItems :false, // stores the menu items..
3433     
3434     hidden:true,
3435         
3436     parentMenu : false,
3437     
3438     stopEvent : true,
3439     
3440     isLink : false,
3441     
3442     getChildContainer : function() {
3443         return this.el;  
3444     },
3445     
3446     getAutoCreate : function(){
3447          
3448         //if (['right'].indexOf(this.align)!==-1) {
3449         //    cfg.cn[1].cls += ' pull-right'
3450         //}
3451         
3452         
3453         var cfg = {
3454             tag : 'ul',
3455             cls : 'dropdown-menu' ,
3456             style : 'z-index:1000'
3457             
3458         };
3459         
3460         if (this.type === 'submenu') {
3461             cfg.cls = 'submenu active';
3462         }
3463         if (this.type === 'treeview') {
3464             cfg.cls = 'treeview-menu';
3465         }
3466         
3467         return cfg;
3468     },
3469     initEvents : function() {
3470         
3471        // Roo.log("ADD event");
3472        // Roo.log(this.triggerEl.dom);
3473         
3474         this.triggerEl.on('click', this.onTriggerClick, this);
3475         
3476         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3477         
3478         
3479         if (this.triggerEl.hasClass('nav-item')) {
3480             // dropdown toggle on the 'a' in BS4?
3481             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3482         } else {
3483             this.triggerEl.addClass('dropdown-toggle');
3484         }
3485         if (Roo.isTouch) {
3486             this.el.on('touchstart'  , this.onTouch, this);
3487         }
3488         this.el.on('click' , this.onClick, this);
3489
3490         this.el.on("mouseover", this.onMouseOver, this);
3491         this.el.on("mouseout", this.onMouseOut, this);
3492         
3493     },
3494     
3495     findTargetItem : function(e)
3496     {
3497         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3498         if(!t){
3499             return false;
3500         }
3501         //Roo.log(t);         Roo.log(t.id);
3502         if(t && t.id){
3503             //Roo.log(this.menuitems);
3504             return this.menuitems.get(t.id);
3505             
3506             //return this.items.get(t.menuItemId);
3507         }
3508         
3509         return false;
3510     },
3511     
3512     onTouch : function(e) 
3513     {
3514         Roo.log("menu.onTouch");
3515         //e.stopEvent(); this make the user popdown broken
3516         this.onClick(e);
3517     },
3518     
3519     onClick : function(e)
3520     {
3521         Roo.log("menu.onClick");
3522         
3523         var t = this.findTargetItem(e);
3524         if(!t || t.isContainer){
3525             return;
3526         }
3527         Roo.log(e);
3528         /*
3529         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3530             if(t == this.activeItem && t.shouldDeactivate(e)){
3531                 this.activeItem.deactivate();
3532                 delete this.activeItem;
3533                 return;
3534             }
3535             if(t.canActivate){
3536                 this.setActiveItem(t, true);
3537             }
3538             return;
3539             
3540             
3541         }
3542         */
3543        
3544         Roo.log('pass click event');
3545         
3546         t.onClick(e);
3547         
3548         this.fireEvent("click", this, t, e);
3549         
3550         var _this = this;
3551         
3552         if(!t.href.length || t.href == '#'){
3553             (function() { _this.hide(); }).defer(100);
3554         }
3555         
3556     },
3557     
3558     onMouseOver : function(e){
3559         var t  = this.findTargetItem(e);
3560         //Roo.log(t);
3561         //if(t){
3562         //    if(t.canActivate && !t.disabled){
3563         //        this.setActiveItem(t, true);
3564         //    }
3565         //}
3566         
3567         this.fireEvent("mouseover", this, e, t);
3568     },
3569     isVisible : function(){
3570         return !this.hidden;
3571     },
3572     onMouseOut : function(e){
3573         var t  = this.findTargetItem(e);
3574         
3575         //if(t ){
3576         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3577         //        this.activeItem.deactivate();
3578         //        delete this.activeItem;
3579         //    }
3580         //}
3581         this.fireEvent("mouseout", this, e, t);
3582     },
3583     
3584     
3585     /**
3586      * Displays this menu relative to another element
3587      * @param {String/HTMLElement/Roo.Element} element The element to align to
3588      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3589      * the element (defaults to this.defaultAlign)
3590      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3591      */
3592     show : function(el, pos, parentMenu)
3593     {
3594         if (false === this.fireEvent("beforeshow", this)) {
3595             Roo.log("show canceled");
3596             return;
3597         }
3598         this.parentMenu = parentMenu;
3599         if(!this.el){
3600             this.render();
3601         }
3602         
3603         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3604     },
3605      /**
3606      * Displays this menu at a specific xy position
3607      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3608      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3609      */
3610     showAt : function(xy, parentMenu, /* private: */_e){
3611         this.parentMenu = parentMenu;
3612         if(!this.el){
3613             this.render();
3614         }
3615         if(_e !== false){
3616             this.fireEvent("beforeshow", this);
3617             //xy = this.el.adjustForConstraints(xy);
3618         }
3619         
3620         //this.el.show();
3621         this.hideMenuItems();
3622         this.hidden = false;
3623         this.triggerEl.addClass('open');
3624         this.el.addClass('show');
3625         
3626         // reassign x when hitting right
3627         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3628             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3629         }
3630         
3631         // reassign y when hitting bottom
3632         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3633             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3634         }
3635         
3636         // but the list may align on trigger left or trigger top... should it be a properity?
3637         
3638         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3639             this.el.setXY(xy);
3640         }
3641         
3642         this.focus();
3643         this.fireEvent("show", this);
3644     },
3645     
3646     focus : function(){
3647         return;
3648         if(!this.hidden){
3649             this.doFocus.defer(50, this);
3650         }
3651     },
3652
3653     doFocus : function(){
3654         if(!this.hidden){
3655             this.focusEl.focus();
3656         }
3657     },
3658
3659     /**
3660      * Hides this menu and optionally all parent menus
3661      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3662      */
3663     hide : function(deep)
3664     {
3665         if (false === this.fireEvent("beforehide", this)) {
3666             Roo.log("hide canceled");
3667             return;
3668         }
3669         this.hideMenuItems();
3670         if(this.el && this.isVisible()){
3671            
3672             if(this.activeItem){
3673                 this.activeItem.deactivate();
3674                 this.activeItem = null;
3675             }
3676             this.triggerEl.removeClass('open');;
3677             this.el.removeClass('show');
3678             this.hidden = true;
3679             this.fireEvent("hide", this);
3680         }
3681         if(deep === true && this.parentMenu){
3682             this.parentMenu.hide(true);
3683         }
3684     },
3685     
3686     onTriggerClick : function(e)
3687     {
3688         Roo.log('trigger click');
3689         
3690         var target = e.getTarget();
3691         
3692         Roo.log(target.nodeName.toLowerCase());
3693         
3694         if(target.nodeName.toLowerCase() === 'i'){
3695             e.preventDefault();
3696         }
3697         
3698     },
3699     
3700     onTriggerPress  : function(e)
3701     {
3702         Roo.log('trigger press');
3703         //Roo.log(e.getTarget());
3704        // Roo.log(this.triggerEl.dom);
3705        
3706         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3707         var pel = Roo.get(e.getTarget());
3708         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3709             Roo.log('is treeview or dropdown?');
3710             return;
3711         }
3712         
3713         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3714             return;
3715         }
3716         
3717         if (this.isVisible()) {
3718             Roo.log('hide');
3719             this.hide();
3720         } else {
3721             Roo.log('show');
3722             this.show(this.triggerEl, '?', false);
3723         }
3724         
3725         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3726             e.stopEvent();
3727         }
3728         
3729     },
3730        
3731     
3732     hideMenuItems : function()
3733     {
3734         Roo.log("hide Menu Items");
3735         if (!this.el) { 
3736             return;
3737         }
3738         
3739         this.el.select('.open',true).each(function(aa) {
3740             
3741             aa.removeClass('open');
3742          
3743         });
3744     },
3745     addxtypeChild : function (tree, cntr) {
3746         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3747           
3748         this.menuitems.add(comp);
3749         return comp;
3750
3751     },
3752     getEl : function()
3753     {
3754         Roo.log(this.el);
3755         return this.el;
3756     },
3757     
3758     clear : function()
3759     {
3760         this.getEl().dom.innerHTML = '';
3761         this.menuitems.clear();
3762     }
3763 });
3764
3765  
3766  /*
3767  * - LGPL
3768  *
3769  * menu item
3770  * 
3771  */
3772
3773
3774 /**
3775  * @class Roo.bootstrap.MenuItem
3776  * @extends Roo.bootstrap.Component
3777  * Bootstrap MenuItem class
3778  * @cfg {String} html the menu label
3779  * @cfg {String} href the link
3780  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3781  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3782  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3783  * @cfg {String} fa favicon to show on left of menu item.
3784  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3785  * 
3786  * 
3787  * @constructor
3788  * Create a new MenuItem
3789  * @param {Object} config The config object
3790  */
3791
3792
3793 Roo.bootstrap.MenuItem = function(config){
3794     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3795     this.addEvents({
3796         // raw events
3797         /**
3798          * @event click
3799          * The raw click event for the entire grid.
3800          * @param {Roo.bootstrap.MenuItem} this
3801          * @param {Roo.EventObject} e
3802          */
3803         "click" : true
3804     });
3805 };
3806
3807 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3808     
3809     href : false,
3810     html : false,
3811     preventDefault: false,
3812     isContainer : false,
3813     active : false,
3814     fa: false,
3815     
3816     getAutoCreate : function(){
3817         
3818         if(this.isContainer){
3819             return {
3820                 tag: 'li',
3821                 cls: 'dropdown-menu-item '
3822             };
3823         }
3824         var ctag = {
3825             tag: 'span',
3826             html: 'Link'
3827         };
3828         
3829         var anc = {
3830             tag : 'a',
3831             cls : 'dropdown-item',
3832             href : '#',
3833             cn : [  ]
3834         };
3835         
3836         if (this.fa !== false) {
3837             anc.cn.push({
3838                 tag : 'i',
3839                 cls : 'fa fa-' + this.fa
3840             });
3841         }
3842         
3843         anc.cn.push(ctag);
3844         
3845         
3846         var cfg= {
3847             tag: 'li',
3848             cls: 'dropdown-menu-item',
3849             cn: [ anc ]
3850         };
3851         if (this.parent().type == 'treeview') {
3852             cfg.cls = 'treeview-menu';
3853         }
3854         if (this.active) {
3855             cfg.cls += ' active';
3856         }
3857         
3858         
3859         
3860         anc.href = this.href || cfg.cn[0].href ;
3861         ctag.html = this.html || cfg.cn[0].html ;
3862         return cfg;
3863     },
3864     
3865     initEvents: function()
3866     {
3867         if (this.parent().type == 'treeview') {
3868             this.el.select('a').on('click', this.onClick, this);
3869         }
3870         
3871         if (this.menu) {
3872             this.menu.parentType = this.xtype;
3873             this.menu.triggerEl = this.el;
3874             this.menu = this.addxtype(Roo.apply({}, this.menu));
3875         }
3876         
3877     },
3878     onClick : function(e)
3879     {
3880         Roo.log('item on click ');
3881         
3882         if(this.preventDefault){
3883             e.preventDefault();
3884         }
3885         //this.parent().hideMenuItems();
3886         
3887         this.fireEvent('click', this, e);
3888     },
3889     getEl : function()
3890     {
3891         return this.el;
3892     } 
3893 });
3894
3895  
3896
3897  /*
3898  * - LGPL
3899  *
3900  * menu separator
3901  * 
3902  */
3903
3904
3905 /**
3906  * @class Roo.bootstrap.MenuSeparator
3907  * @extends Roo.bootstrap.Component
3908  * Bootstrap MenuSeparator class
3909  * 
3910  * @constructor
3911  * Create a new MenuItem
3912  * @param {Object} config The config object
3913  */
3914
3915
3916 Roo.bootstrap.MenuSeparator = function(config){
3917     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3918 };
3919
3920 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3921     
3922     getAutoCreate : function(){
3923         var cfg = {
3924             cls: 'divider',
3925             tag : 'li'
3926         };
3927         
3928         return cfg;
3929     }
3930    
3931 });
3932
3933  
3934
3935  
3936 /*
3937 * Licence: LGPL
3938 */
3939
3940 /**
3941  * @class Roo.bootstrap.Modal
3942  * @extends Roo.bootstrap.Component
3943  * Bootstrap Modal class
3944  * @cfg {String} title Title of dialog
3945  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3946  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3947  * @cfg {Boolean} specificTitle default false
3948  * @cfg {Array} buttons Array of buttons or standard button set..
3949  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3950  * @cfg {Boolean} animate default true
3951  * @cfg {Boolean} allow_close default true
3952  * @cfg {Boolean} fitwindow default false
3953  * @cfg {Boolean} bodyOverflow should the body element have overflow auto added default false
3954  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3955  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3956  * @cfg {String} size (sm|lg|xl) default empty
3957  * @cfg {Number} max_width set the max width of modal
3958  * @cfg {Boolean} editableTitle can the title be edited
3959
3960  *
3961  *
3962  * @constructor
3963  * Create a new Modal Dialog
3964  * @param {Object} config The config object
3965  */
3966
3967 Roo.bootstrap.Modal = function(config){
3968     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3969     this.addEvents({
3970         // raw events
3971         /**
3972          * @event btnclick
3973          * The raw btnclick event for the button
3974          * @param {Roo.EventObject} e
3975          */
3976         "btnclick" : true,
3977         /**
3978          * @event resize
3979          * Fire when dialog resize
3980          * @param {Roo.bootstrap.Modal} this
3981          * @param {Roo.EventObject} e
3982          */
3983         "resize" : true,
3984         /**
3985          * @event titlechanged
3986          * Fire when the editable title has been changed
3987          * @param {Roo.bootstrap.Modal} this
3988          * @param {Roo.EventObject} value
3989          */
3990         "titlechanged" : true 
3991         
3992     });
3993     this.buttons = this.buttons || [];
3994
3995     if (this.tmpl) {
3996         this.tmpl = Roo.factory(this.tmpl);
3997     }
3998
3999 };
4000
4001 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
4002
4003     title : 'test dialog',
4004
4005     buttons : false,
4006
4007     // set on load...
4008
4009     html: false,
4010
4011     tmp: false,
4012
4013     specificTitle: false,
4014
4015     buttonPosition: 'right',
4016
4017     allow_close : true,
4018
4019     animate : true,
4020
4021     fitwindow: false,
4022     
4023      // private
4024     dialogEl: false,
4025     bodyEl:  false,
4026     footerEl:  false,
4027     titleEl:  false,
4028     closeEl:  false,
4029
4030     size: '',
4031     
4032     max_width: 0,
4033     
4034     max_height: 0,
4035     
4036     fit_content: false,
4037     editableTitle  : false,
4038
4039     onRender : function(ct, position)
4040     {
4041         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4042
4043         if(!this.el){
4044             var cfg = Roo.apply({},  this.getAutoCreate());
4045             cfg.id = Roo.id();
4046             //if(!cfg.name){
4047             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4048             //}
4049             //if (!cfg.name.length) {
4050             //    delete cfg.name;
4051            // }
4052             if (this.cls) {
4053                 cfg.cls += ' ' + this.cls;
4054             }
4055             if (this.style) {
4056                 cfg.style = this.style;
4057             }
4058             this.el = Roo.get(document.body).createChild(cfg, position);
4059         }
4060         //var type = this.el.dom.type;
4061
4062
4063         if(this.tabIndex !== undefined){
4064             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4065         }
4066
4067         this.dialogEl = this.el.select('.modal-dialog',true).first();
4068         this.bodyEl = this.el.select('.modal-body',true).first();
4069         this.closeEl = this.el.select('.modal-header .close', true).first();
4070         this.headerEl = this.el.select('.modal-header',true).first();
4071         this.titleEl = this.el.select('.modal-title',true).first();
4072         this.footerEl = this.el.select('.modal-footer',true).first();
4073
4074         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4075         
4076         //this.el.addClass("x-dlg-modal");
4077
4078         if (this.buttons.length) {
4079             Roo.each(this.buttons, function(bb) {
4080                 var b = Roo.apply({}, bb);
4081                 b.xns = b.xns || Roo.bootstrap;
4082                 b.xtype = b.xtype || 'Button';
4083                 if (typeof(b.listeners) == 'undefined') {
4084                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4085                 }
4086
4087                 var btn = Roo.factory(b);
4088
4089                 btn.render(this.getButtonContainer());
4090
4091             },this);
4092         }
4093         // render the children.
4094         var nitems = [];
4095
4096         if(typeof(this.items) != 'undefined'){
4097             var items = this.items;
4098             delete this.items;
4099
4100             for(var i =0;i < items.length;i++) {
4101                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4102             }
4103         }
4104
4105         this.items = nitems;
4106
4107         // where are these used - they used to be body/close/footer
4108
4109
4110         this.initEvents();
4111         //this.el.addClass([this.fieldClass, this.cls]);
4112
4113     },
4114
4115     getAutoCreate : function()
4116     {
4117         // we will default to modal-body-overflow - might need to remove or make optional later.
4118         var bdy = {
4119                 cls : 'modal-body ' + (this.bodyOverflow ? 'overflow-auto' : ''), 
4120                 html : this.html || ''
4121         };
4122
4123         var title = {
4124             tag: 'h5',
4125             cls : 'modal-title',
4126             html : this.title
4127         };
4128
4129         if(this.specificTitle){ // WTF is this?
4130             title = this.title;
4131         }
4132
4133         var header = [];
4134         if (this.allow_close && Roo.bootstrap.version == 3) {
4135             header.push({
4136                 tag: 'button',
4137                 cls : 'close',
4138                 html : '&times'
4139             });
4140         }
4141
4142         header.push(title);
4143
4144         if (this.editableTitle) {
4145             header.push({
4146                 cls: 'form-control roo-editable-title d-none',
4147                 tag: 'input',
4148                 type: 'text'
4149             });
4150         }
4151         
4152         if (this.allow_close && Roo.bootstrap.version == 4) {
4153             header.push({
4154                 tag: 'button',
4155                 cls : 'close',
4156                 html : '&times'
4157             });
4158         }
4159         
4160         var size = '';
4161
4162         if(this.size.length){
4163             size = 'modal-' + this.size;
4164         }
4165         
4166         var footer = Roo.bootstrap.version == 3 ?
4167             {
4168                 cls : 'modal-footer',
4169                 cn : [
4170                     {
4171                         tag: 'div',
4172                         cls: 'btn-' + this.buttonPosition
4173                     }
4174                 ]
4175
4176             } :
4177             {  // BS4 uses mr-auto on left buttons....
4178                 cls : 'modal-footer'
4179             };
4180
4181             
4182
4183         
4184         
4185         var modal = {
4186             cls: "modal",
4187              cn : [
4188                 {
4189                     cls: "modal-dialog " + size,
4190                     cn : [
4191                         {
4192                             cls : "modal-content",
4193                             cn : [
4194                                 {
4195                                     cls : 'modal-header',
4196                                     cn : header
4197                                 },
4198                                 bdy,
4199                                 footer
4200                             ]
4201
4202                         }
4203                     ]
4204
4205                 }
4206             ]
4207         };
4208
4209         if(this.animate){
4210             modal.cls += ' fade';
4211         }
4212
4213         return modal;
4214
4215     },
4216     getChildContainer : function() {
4217
4218          return this.bodyEl;
4219
4220     },
4221     getButtonContainer : function() {
4222         
4223          return Roo.bootstrap.version == 4 ?
4224             this.el.select('.modal-footer',true).first()
4225             : this.el.select('.modal-footer div',true).first();
4226
4227     },
4228     initEvents : function()
4229     {
4230         if (this.allow_close) {
4231             this.closeEl.on('click', this.hide, this);
4232         }
4233         Roo.EventManager.onWindowResize(this.resize, this, true);
4234         if (this.editableTitle) {
4235             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4236             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4237             this.headerEditEl.on('keyup', function(e) {
4238                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4239                         this.toggleHeaderInput(false)
4240                     }
4241                 }, this);
4242             this.headerEditEl.on('blur', function(e) {
4243                 this.toggleHeaderInput(false)
4244             },this);
4245         }
4246
4247     },
4248   
4249
4250     resize : function()
4251     {
4252         this.maskEl.setSize(
4253             Roo.lib.Dom.getViewWidth(true),
4254             Roo.lib.Dom.getViewHeight(true)
4255         );
4256         
4257         if (this.fitwindow) {
4258             
4259            this.dialogEl.setStyle( { 'max-width' : '100%' });
4260             this.setSize(
4261                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4262                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4263             );
4264             return;
4265         }
4266         
4267         if(this.max_width !== 0) {
4268             
4269             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4270             
4271             if(this.height) {
4272                 this.setSize(w, this.height);
4273                 return;
4274             }
4275             
4276             if(this.max_height) {
4277                 this.setSize(w,Math.min(
4278                     this.max_height,
4279                     Roo.lib.Dom.getViewportHeight(true) - 60
4280                 ));
4281                 
4282                 return;
4283             }
4284             
4285             if(!this.fit_content) {
4286                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4287                 return;
4288             }
4289             
4290             this.setSize(w, Math.min(
4291                 60 +
4292                 this.headerEl.getHeight() + 
4293                 this.footerEl.getHeight() + 
4294                 this.getChildHeight(this.bodyEl.dom.childNodes),
4295                 Roo.lib.Dom.getViewportHeight(true) - 60)
4296             );
4297         }
4298         
4299     },
4300
4301     setSize : function(w,h)
4302     {
4303         if (!w && !h) {
4304             return;
4305         }
4306         
4307         this.resizeTo(w,h);
4308     },
4309
4310     show : function() {
4311
4312         if (!this.rendered) {
4313             this.render();
4314         }
4315         this.toggleHeaderInput(false);
4316         //this.el.setStyle('display', 'block');
4317         this.el.removeClass('hideing');
4318         this.el.dom.style.display='block';
4319         
4320         Roo.get(document.body).addClass('modal-open');
4321  
4322         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4323             
4324             (function(){
4325                 this.el.addClass('show');
4326                 this.el.addClass('in');
4327             }).defer(50, this);
4328         }else{
4329             this.el.addClass('show');
4330             this.el.addClass('in');
4331         }
4332
4333         // not sure how we can show data in here..
4334         //if (this.tmpl) {
4335         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4336         //}
4337
4338         Roo.get(document.body).addClass("x-body-masked");
4339         
4340         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4341         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4342         this.maskEl.dom.style.display = 'block';
4343         this.maskEl.addClass('show');
4344         
4345         
4346         this.resize();
4347         
4348         this.fireEvent('show', this);
4349
4350         // set zindex here - otherwise it appears to be ignored...
4351         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4352
4353         (function () {
4354             this.items.forEach( function(e) {
4355                 e.layout ? e.layout() : false;
4356
4357             });
4358         }).defer(100,this);
4359
4360     },
4361     hide : function()
4362     {
4363         if(this.fireEvent("beforehide", this) !== false){
4364             
4365             this.maskEl.removeClass('show');
4366             
4367             this.maskEl.dom.style.display = '';
4368             Roo.get(document.body).removeClass("x-body-masked");
4369             this.el.removeClass('in');
4370             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4371
4372             if(this.animate){ // why
4373                 this.el.addClass('hideing');
4374                 this.el.removeClass('show');
4375                 (function(){
4376                     if (!this.el.hasClass('hideing')) {
4377                         return; // it's been shown again...
4378                     }
4379                     
4380                     this.el.dom.style.display='';
4381
4382                     Roo.get(document.body).removeClass('modal-open');
4383                     this.el.removeClass('hideing');
4384                 }).defer(150,this);
4385                 
4386             }else{
4387                 this.el.removeClass('show');
4388                 this.el.dom.style.display='';
4389                 Roo.get(document.body).removeClass('modal-open');
4390
4391             }
4392             this.fireEvent('hide', this);
4393         }
4394     },
4395     isVisible : function()
4396     {
4397         
4398         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4399         
4400     },
4401
4402     addButton : function(str, cb)
4403     {
4404
4405
4406         var b = Roo.apply({}, { html : str } );
4407         b.xns = b.xns || Roo.bootstrap;
4408         b.xtype = b.xtype || 'Button';
4409         if (typeof(b.listeners) == 'undefined') {
4410             b.listeners = { click : cb.createDelegate(this)  };
4411         }
4412
4413         var btn = Roo.factory(b);
4414
4415         btn.render(this.getButtonContainer());
4416
4417         return btn;
4418
4419     },
4420
4421     setDefaultButton : function(btn)
4422     {
4423         //this.el.select('.modal-footer').()
4424     },
4425
4426     resizeTo: function(w,h)
4427     {
4428         this.dialogEl.setWidth(w);
4429         
4430         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4431
4432         this.bodyEl.setHeight(h - diff);
4433         
4434         this.fireEvent('resize', this);
4435     },
4436     
4437     setContentSize  : function(w, h)
4438     {
4439
4440     },
4441     onButtonClick: function(btn,e)
4442     {
4443         //Roo.log([a,b,c]);
4444         this.fireEvent('btnclick', btn.name, e);
4445     },
4446      /**
4447      * Set the title of the Dialog
4448      * @param {String} str new Title
4449      */
4450     setTitle: function(str) {
4451         this.titleEl.dom.innerHTML = str;
4452         this.title = str;
4453     },
4454     /**
4455      * Set the body of the Dialog
4456      * @param {String} str new Title
4457      */
4458     setBody: function(str) {
4459         this.bodyEl.dom.innerHTML = str;
4460     },
4461     /**
4462      * Set the body of the Dialog using the template
4463      * @param {Obj} data - apply this data to the template and replace the body contents.
4464      */
4465     applyBody: function(obj)
4466     {
4467         if (!this.tmpl) {
4468             Roo.log("Error - using apply Body without a template");
4469             //code
4470         }
4471         this.tmpl.overwrite(this.bodyEl, obj);
4472     },
4473     
4474     getChildHeight : function(child_nodes)
4475     {
4476         if(
4477             !child_nodes ||
4478             child_nodes.length == 0
4479         ) {
4480             return 0;
4481         }
4482         
4483         var child_height = 0;
4484         
4485         for(var i = 0; i < child_nodes.length; i++) {
4486             
4487             /*
4488             * for modal with tabs...
4489             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4490                 
4491                 var layout_childs = child_nodes[i].childNodes;
4492                 
4493                 for(var j = 0; j < layout_childs.length; j++) {
4494                     
4495                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4496                         
4497                         var layout_body_childs = layout_childs[j].childNodes;
4498                         
4499                         for(var k = 0; k < layout_body_childs.length; k++) {
4500                             
4501                             if(layout_body_childs[k].classList.contains('navbar')) {
4502                                 child_height += layout_body_childs[k].offsetHeight;
4503                                 continue;
4504                             }
4505                             
4506                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4507                                 
4508                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4509                                 
4510                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4511                                     
4512                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4513                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4514                                         continue;
4515                                     }
4516                                     
4517                                 }
4518                                 
4519                             }
4520                             
4521                         }
4522                     }
4523                 }
4524                 continue;
4525             }
4526             */
4527             
4528             child_height += child_nodes[i].offsetHeight;
4529             // Roo.log(child_nodes[i].offsetHeight);
4530         }
4531         
4532         return child_height;
4533     },
4534     toggleHeaderInput : function(is_edit)
4535     {
4536         if (!this.editableTitle) {
4537             return; // not editable.
4538         }
4539         if (is_edit && this.is_header_editing) {
4540             return; // already editing..
4541         }
4542         if (is_edit) {
4543     
4544             this.headerEditEl.dom.value = this.title;
4545             this.headerEditEl.removeClass('d-none');
4546             this.headerEditEl.dom.focus();
4547             this.titleEl.addClass('d-none');
4548             
4549             this.is_header_editing = true;
4550             return
4551         }
4552         // flip back to not editing.
4553         this.title = this.headerEditEl.dom.value;
4554         this.headerEditEl.addClass('d-none');
4555         this.titleEl.removeClass('d-none');
4556         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4557         this.is_header_editing = false;
4558         this.fireEvent('titlechanged', this, this.title);
4559     
4560             
4561         
4562     }
4563
4564 });
4565
4566
4567 Roo.apply(Roo.bootstrap.Modal,  {
4568     /**
4569          * Button config that displays a single OK button
4570          * @type Object
4571          */
4572         OK :  [{
4573             name : 'ok',
4574             weight : 'primary',
4575             html : 'OK'
4576         }],
4577         /**
4578          * Button config that displays Yes and No buttons
4579          * @type Object
4580          */
4581         YESNO : [
4582             {
4583                 name  : 'no',
4584                 html : 'No'
4585             },
4586             {
4587                 name  :'yes',
4588                 weight : 'primary',
4589                 html : 'Yes'
4590             }
4591         ],
4592
4593         /**
4594          * Button config that displays OK and Cancel buttons
4595          * @type Object
4596          */
4597         OKCANCEL : [
4598             {
4599                name : 'cancel',
4600                 html : 'Cancel'
4601             },
4602             {
4603                 name : 'ok',
4604                 weight : 'primary',
4605                 html : 'OK'
4606             }
4607         ],
4608         /**
4609          * Button config that displays Yes, No and Cancel buttons
4610          * @type Object
4611          */
4612         YESNOCANCEL : [
4613             {
4614                 name : 'yes',
4615                 weight : 'primary',
4616                 html : 'Yes'
4617             },
4618             {
4619                 name : 'no',
4620                 html : 'No'
4621             },
4622             {
4623                 name : 'cancel',
4624                 html : 'Cancel'
4625             }
4626         ],
4627         
4628         zIndex : 10001
4629 });
4630
4631 /*
4632  * - LGPL
4633  *
4634  * messagebox - can be used as a replace
4635  * 
4636  */
4637 /**
4638  * @class Roo.MessageBox
4639  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4640  * Example usage:
4641  *<pre><code>
4642 // Basic alert:
4643 Roo.Msg.alert('Status', 'Changes saved successfully.');
4644
4645 // Prompt for user data:
4646 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4647     if (btn == 'ok'){
4648         // process text value...
4649     }
4650 });
4651
4652 // Show a dialog using config options:
4653 Roo.Msg.show({
4654    title:'Save Changes?',
4655    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4656    buttons: Roo.Msg.YESNOCANCEL,
4657    fn: processResult,
4658    animEl: 'elId'
4659 });
4660 </code></pre>
4661  * @singleton
4662  */
4663 Roo.bootstrap.MessageBox = function(){
4664     var dlg, opt, mask, waitTimer;
4665     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4666     var buttons, activeTextEl, bwidth;
4667
4668     
4669     // private
4670     var handleButton = function(button){
4671         dlg.hide();
4672         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4673     };
4674
4675     // private
4676     var handleHide = function(){
4677         if(opt && opt.cls){
4678             dlg.el.removeClass(opt.cls);
4679         }
4680         //if(waitTimer){
4681         //    Roo.TaskMgr.stop(waitTimer);
4682         //    waitTimer = null;
4683         //}
4684     };
4685
4686     // private
4687     var updateButtons = function(b){
4688         var width = 0;
4689         if(!b){
4690             buttons["ok"].hide();
4691             buttons["cancel"].hide();
4692             buttons["yes"].hide();
4693             buttons["no"].hide();
4694             dlg.footerEl.hide();
4695             
4696             return width;
4697         }
4698         dlg.footerEl.show();
4699         for(var k in buttons){
4700             if(typeof buttons[k] != "function"){
4701                 if(b[k]){
4702                     buttons[k].show();
4703                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4704                     width += buttons[k].el.getWidth()+15;
4705                 }else{
4706                     buttons[k].hide();
4707                 }
4708             }
4709         }
4710         return width;
4711     };
4712
4713     // private
4714     var handleEsc = function(d, k, e){
4715         if(opt && opt.closable !== false){
4716             dlg.hide();
4717         }
4718         if(e){
4719             e.stopEvent();
4720         }
4721     };
4722
4723     return {
4724         /**
4725          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4726          * @return {Roo.BasicDialog} The BasicDialog element
4727          */
4728         getDialog : function(){
4729            if(!dlg){
4730                 dlg = new Roo.bootstrap.Modal( {
4731                     //draggable: true,
4732                     //resizable:false,
4733                     //constraintoviewport:false,
4734                     //fixedcenter:true,
4735                     //collapsible : false,
4736                     //shim:true,
4737                     //modal: true,
4738                 //    width: 'auto',
4739                   //  height:100,
4740                     //buttonAlign:"center",
4741                     closeClick : function(){
4742                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4743                             handleButton("no");
4744                         }else{
4745                             handleButton("cancel");
4746                         }
4747                     }
4748                 });
4749                 dlg.render();
4750                 dlg.on("hide", handleHide);
4751                 mask = dlg.mask;
4752                 //dlg.addKeyListener(27, handleEsc);
4753                 buttons = {};
4754                 this.buttons = buttons;
4755                 var bt = this.buttonText;
4756                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4757                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4758                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4759                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4760                 //Roo.log(buttons);
4761                 bodyEl = dlg.bodyEl.createChild({
4762
4763                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4764                         '<textarea class="roo-mb-textarea"></textarea>' +
4765                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4766                 });
4767                 msgEl = bodyEl.dom.firstChild;
4768                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4769                 textboxEl.enableDisplayMode();
4770                 textboxEl.addKeyListener([10,13], function(){
4771                     if(dlg.isVisible() && opt && opt.buttons){
4772                         if(opt.buttons.ok){
4773                             handleButton("ok");
4774                         }else if(opt.buttons.yes){
4775                             handleButton("yes");
4776                         }
4777                     }
4778                 });
4779                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4780                 textareaEl.enableDisplayMode();
4781                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4782                 progressEl.enableDisplayMode();
4783                 
4784                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4785                 var pf = progressEl.dom.firstChild;
4786                 if (pf) {
4787                     pp = Roo.get(pf.firstChild);
4788                     pp.setHeight(pf.offsetHeight);
4789                 }
4790                 
4791             }
4792             return dlg;
4793         },
4794
4795         /**
4796          * Updates the message box body text
4797          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4798          * the XHTML-compliant non-breaking space character '&amp;#160;')
4799          * @return {Roo.MessageBox} This message box
4800          */
4801         updateText : function(text)
4802         {
4803             if(!dlg.isVisible() && !opt.width){
4804                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4805                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4806             }
4807             msgEl.innerHTML = text || '&#160;';
4808       
4809             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4810             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4811             var w = Math.max(
4812                     Math.min(opt.width || cw , this.maxWidth), 
4813                     Math.max(opt.minWidth || this.minWidth, bwidth)
4814             );
4815             if(opt.prompt){
4816                 activeTextEl.setWidth(w);
4817             }
4818             if(dlg.isVisible()){
4819                 dlg.fixedcenter = false;
4820             }
4821             // to big, make it scroll. = But as usual stupid IE does not support
4822             // !important..
4823             
4824             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4825                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4826                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4827             } else {
4828                 bodyEl.dom.style.height = '';
4829                 bodyEl.dom.style.overflowY = '';
4830             }
4831             if (cw > w) {
4832                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4833             } else {
4834                 bodyEl.dom.style.overflowX = '';
4835             }
4836             
4837             dlg.setContentSize(w, bodyEl.getHeight());
4838             if(dlg.isVisible()){
4839                 dlg.fixedcenter = true;
4840             }
4841             return this;
4842         },
4843
4844         /**
4845          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4846          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4847          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4848          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4849          * @return {Roo.MessageBox} This message box
4850          */
4851         updateProgress : function(value, text){
4852             if(text){
4853                 this.updateText(text);
4854             }
4855             
4856             if (pp) { // weird bug on my firefox - for some reason this is not defined
4857                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4858                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4859             }
4860             return this;
4861         },        
4862
4863         /**
4864          * Returns true if the message box is currently displayed
4865          * @return {Boolean} True if the message box is visible, else false
4866          */
4867         isVisible : function(){
4868             return dlg && dlg.isVisible();  
4869         },
4870
4871         /**
4872          * Hides the message box if it is displayed
4873          */
4874         hide : function(){
4875             if(this.isVisible()){
4876                 dlg.hide();
4877             }  
4878         },
4879
4880         /**
4881          * Displays a new message box, or reinitializes an existing message box, based on the config options
4882          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4883          * The following config object properties are supported:
4884          * <pre>
4885 Property    Type             Description
4886 ----------  ---------------  ------------------------------------------------------------------------------------
4887 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4888                                    closes (defaults to undefined)
4889 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4890                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4891 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4892                                    progress and wait dialogs will ignore this property and always hide the
4893                                    close button as they can only be closed programmatically.
4894 cls               String           A custom CSS class to apply to the message box element
4895 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4896                                    displayed (defaults to 75)
4897 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4898                                    function will be btn (the name of the button that was clicked, if applicable,
4899                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4900                                    Progress and wait dialogs will ignore this option since they do not respond to
4901                                    user actions and can only be closed programmatically, so any required function
4902                                    should be called by the same code after it closes the dialog.
4903 icon              String           A CSS class that provides a background image to be used as an icon for
4904                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4905 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4906 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4907 modal             Boolean          False to allow user interaction with the page while the message box is
4908                                    displayed (defaults to true)
4909 msg               String           A string that will replace the existing message box body text (defaults
4910                                    to the XHTML-compliant non-breaking space character '&#160;')
4911 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4912 progress          Boolean          True to display a progress bar (defaults to false)
4913 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4914 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4915 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4916 title             String           The title text
4917 value             String           The string value to set into the active textbox element if displayed
4918 wait              Boolean          True to display a progress bar (defaults to false)
4919 width             Number           The width of the dialog in pixels
4920 </pre>
4921          *
4922          * Example usage:
4923          * <pre><code>
4924 Roo.Msg.show({
4925    title: 'Address',
4926    msg: 'Please enter your address:',
4927    width: 300,
4928    buttons: Roo.MessageBox.OKCANCEL,
4929    multiline: true,
4930    fn: saveAddress,
4931    animEl: 'addAddressBtn'
4932 });
4933 </code></pre>
4934          * @param {Object} config Configuration options
4935          * @return {Roo.MessageBox} This message box
4936          */
4937         show : function(options)
4938         {
4939             
4940             // this causes nightmares if you show one dialog after another
4941             // especially on callbacks..
4942              
4943             if(this.isVisible()){
4944                 
4945                 this.hide();
4946                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4947                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4948                 Roo.log("New Dialog Message:" +  options.msg )
4949                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4950                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4951                 
4952             }
4953             var d = this.getDialog();
4954             opt = options;
4955             d.setTitle(opt.title || "&#160;");
4956             d.closeEl.setDisplayed(opt.closable !== false);
4957             activeTextEl = textboxEl;
4958             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4959             if(opt.prompt){
4960                 if(opt.multiline){
4961                     textboxEl.hide();
4962                     textareaEl.show();
4963                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4964                         opt.multiline : this.defaultTextHeight);
4965                     activeTextEl = textareaEl;
4966                 }else{
4967                     textboxEl.show();
4968                     textareaEl.hide();
4969                 }
4970             }else{
4971                 textboxEl.hide();
4972                 textareaEl.hide();
4973             }
4974             progressEl.setDisplayed(opt.progress === true);
4975             if (opt.progress) {
4976                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4977             }
4978             this.updateProgress(0);
4979             activeTextEl.dom.value = opt.value || "";
4980             if(opt.prompt){
4981                 dlg.setDefaultButton(activeTextEl);
4982             }else{
4983                 var bs = opt.buttons;
4984                 var db = null;
4985                 if(bs && bs.ok){
4986                     db = buttons["ok"];
4987                 }else if(bs && bs.yes){
4988                     db = buttons["yes"];
4989                 }
4990                 dlg.setDefaultButton(db);
4991             }
4992             bwidth = updateButtons(opt.buttons);
4993             this.updateText(opt.msg);
4994             if(opt.cls){
4995                 d.el.addClass(opt.cls);
4996             }
4997             d.proxyDrag = opt.proxyDrag === true;
4998             d.modal = opt.modal !== false;
4999             d.mask = opt.modal !== false ? mask : false;
5000             if(!d.isVisible()){
5001                 // force it to the end of the z-index stack so it gets a cursor in FF
5002                 document.body.appendChild(dlg.el.dom);
5003                 d.animateTarget = null;
5004                 d.show(options.animEl);
5005             }
5006             return this;
5007         },
5008
5009         /**
5010          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5011          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5012          * and closing the message box when the process is complete.
5013          * @param {String} title The title bar text
5014          * @param {String} msg The message box body text
5015          * @return {Roo.MessageBox} This message box
5016          */
5017         progress : function(title, msg){
5018             this.show({
5019                 title : title,
5020                 msg : msg,
5021                 buttons: false,
5022                 progress:true,
5023                 closable:false,
5024                 minWidth: this.minProgressWidth,
5025                 modal : true
5026             });
5027             return this;
5028         },
5029
5030         /**
5031          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5032          * If a callback function is passed it will be called after the user clicks the button, and the
5033          * id of the button that was clicked will be passed as the only parameter to the callback
5034          * (could also be the top-right close button).
5035          * @param {String} title The title bar text
5036          * @param {String} msg The message box body text
5037          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5038          * @param {Object} scope (optional) The scope of the callback function
5039          * @return {Roo.MessageBox} This message box
5040          */
5041         alert : function(title, msg, fn, scope)
5042         {
5043             this.show({
5044                 title : title,
5045                 msg : msg,
5046                 buttons: this.OK,
5047                 fn: fn,
5048                 closable : false,
5049                 scope : scope,
5050                 modal : true
5051             });
5052             return this;
5053         },
5054
5055         /**
5056          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5057          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5058          * You are responsible for closing the message box when the process is complete.
5059          * @param {String} msg The message box body text
5060          * @param {String} title (optional) The title bar text
5061          * @return {Roo.MessageBox} This message box
5062          */
5063         wait : function(msg, title){
5064             this.show({
5065                 title : title,
5066                 msg : msg,
5067                 buttons: false,
5068                 closable:false,
5069                 progress:true,
5070                 modal:true,
5071                 width:300,
5072                 wait:true
5073             });
5074             waitTimer = Roo.TaskMgr.start({
5075                 run: function(i){
5076                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5077                 },
5078                 interval: 1000
5079             });
5080             return this;
5081         },
5082
5083         /**
5084          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5085          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5086          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5087          * @param {String} title The title bar text
5088          * @param {String} msg The message box body text
5089          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5090          * @param {Object} scope (optional) The scope of the callback function
5091          * @return {Roo.MessageBox} This message box
5092          */
5093         confirm : function(title, msg, fn, scope){
5094             this.show({
5095                 title : title,
5096                 msg : msg,
5097                 buttons: this.YESNO,
5098                 fn: fn,
5099                 scope : scope,
5100                 modal : true
5101             });
5102             return this;
5103         },
5104
5105         /**
5106          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5107          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5108          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5109          * (could also be the top-right close button) and the text that was entered will be passed as the two
5110          * parameters to the callback.
5111          * @param {String} title The title bar text
5112          * @param {String} msg The message box body text
5113          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5114          * @param {Object} scope (optional) The scope of the callback function
5115          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5116          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5117          * @return {Roo.MessageBox} This message box
5118          */
5119         prompt : function(title, msg, fn, scope, multiline){
5120             this.show({
5121                 title : title,
5122                 msg : msg,
5123                 buttons: this.OKCANCEL,
5124                 fn: fn,
5125                 minWidth:250,
5126                 scope : scope,
5127                 prompt:true,
5128                 multiline: multiline,
5129                 modal : true
5130             });
5131             return this;
5132         },
5133
5134         /**
5135          * Button config that displays a single OK button
5136          * @type Object
5137          */
5138         OK : {ok:true},
5139         /**
5140          * Button config that displays Yes and No buttons
5141          * @type Object
5142          */
5143         YESNO : {yes:true, no:true},
5144         /**
5145          * Button config that displays OK and Cancel buttons
5146          * @type Object
5147          */
5148         OKCANCEL : {ok:true, cancel:true},
5149         /**
5150          * Button config that displays Yes, No and Cancel buttons
5151          * @type Object
5152          */
5153         YESNOCANCEL : {yes:true, no:true, cancel:true},
5154
5155         /**
5156          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5157          * @type Number
5158          */
5159         defaultTextHeight : 75,
5160         /**
5161          * The maximum width in pixels of the message box (defaults to 600)
5162          * @type Number
5163          */
5164         maxWidth : 600,
5165         /**
5166          * The minimum width in pixels of the message box (defaults to 100)
5167          * @type Number
5168          */
5169         minWidth : 100,
5170         /**
5171          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5172          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5173          * @type Number
5174          */
5175         minProgressWidth : 250,
5176         /**
5177          * An object containing the default button text strings that can be overriden for localized language support.
5178          * Supported properties are: ok, cancel, yes and no.
5179          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5180          * @type Object
5181          */
5182         buttonText : {
5183             ok : "OK",
5184             cancel : "Cancel",
5185             yes : "Yes",
5186             no : "No"
5187         }
5188     };
5189 }();
5190
5191 /**
5192  * Shorthand for {@link Roo.MessageBox}
5193  */
5194 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5195 Roo.Msg = Roo.Msg || Roo.MessageBox;
5196 /*
5197  * - LGPL
5198  *
5199  * navbar
5200  * 
5201  */
5202
5203 /**
5204  * @class Roo.bootstrap.Navbar
5205  * @extends Roo.bootstrap.Component
5206  * Bootstrap Navbar class
5207
5208  * @constructor
5209  * Create a new Navbar
5210  * @param {Object} config The config object
5211  */
5212
5213
5214 Roo.bootstrap.Navbar = function(config){
5215     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5216     this.addEvents({
5217         // raw events
5218         /**
5219          * @event beforetoggle
5220          * Fire before toggle the menu
5221          * @param {Roo.EventObject} e
5222          */
5223         "beforetoggle" : true
5224     });
5225 };
5226
5227 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5228     
5229     
5230    
5231     // private
5232     navItems : false,
5233     loadMask : false,
5234     
5235     
5236     getAutoCreate : function(){
5237         
5238         
5239         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5240         
5241     },
5242     
5243     initEvents :function ()
5244     {
5245         //Roo.log(this.el.select('.navbar-toggle',true));
5246         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5247         
5248         var mark = {
5249             tag: "div",
5250             cls:"x-dlg-mask"
5251         };
5252         
5253         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5254         
5255         var size = this.el.getSize();
5256         this.maskEl.setSize(size.width, size.height);
5257         this.maskEl.enableDisplayMode("block");
5258         this.maskEl.hide();
5259         
5260         if(this.loadMask){
5261             this.maskEl.show();
5262         }
5263     },
5264     
5265     
5266     getChildContainer : function()
5267     {
5268         if (this.el && this.el.select('.collapse').getCount()) {
5269             return this.el.select('.collapse',true).first();
5270         }
5271         
5272         return this.el;
5273     },
5274     
5275     mask : function()
5276     {
5277         this.maskEl.show();
5278     },
5279     
5280     unmask : function()
5281     {
5282         this.maskEl.hide();
5283     },
5284     onToggle : function()
5285     {
5286         
5287         if(this.fireEvent('beforetoggle', this) === false){
5288             return;
5289         }
5290         var ce = this.el.select('.navbar-collapse',true).first();
5291       
5292         if (!ce.hasClass('show')) {
5293            this.expand();
5294         } else {
5295             this.collapse();
5296         }
5297         
5298         
5299     
5300     },
5301     /**
5302      * Expand the navbar pulldown 
5303      */
5304     expand : function ()
5305     {
5306        
5307         var ce = this.el.select('.navbar-collapse',true).first();
5308         if (ce.hasClass('collapsing')) {
5309             return;
5310         }
5311         ce.dom.style.height = '';
5312                // show it...
5313         ce.addClass('in'); // old...
5314         ce.removeClass('collapse');
5315         ce.addClass('show');
5316         var h = ce.getHeight();
5317         Roo.log(h);
5318         ce.removeClass('show');
5319         // at this point we should be able to see it..
5320         ce.addClass('collapsing');
5321         
5322         ce.setHeight(0); // resize it ...
5323         ce.on('transitionend', function() {
5324             //Roo.log('done transition');
5325             ce.removeClass('collapsing');
5326             ce.addClass('show');
5327             ce.removeClass('collapse');
5328
5329             ce.dom.style.height = '';
5330         }, this, { single: true} );
5331         ce.setHeight(h);
5332         ce.dom.scrollTop = 0;
5333     },
5334     /**
5335      * Collapse the navbar pulldown 
5336      */
5337     collapse : function()
5338     {
5339          var ce = this.el.select('.navbar-collapse',true).first();
5340        
5341         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5342             // it's collapsed or collapsing..
5343             return;
5344         }
5345         ce.removeClass('in'); // old...
5346         ce.setHeight(ce.getHeight());
5347         ce.removeClass('show');
5348         ce.addClass('collapsing');
5349         
5350         ce.on('transitionend', function() {
5351             ce.dom.style.height = '';
5352             ce.removeClass('collapsing');
5353             ce.addClass('collapse');
5354         }, this, { single: true} );
5355         ce.setHeight(0);
5356     }
5357     
5358     
5359     
5360 });
5361
5362
5363
5364  
5365
5366  /*
5367  * - LGPL
5368  *
5369  * navbar
5370  * 
5371  */
5372
5373 /**
5374  * @class Roo.bootstrap.NavSimplebar
5375  * @extends Roo.bootstrap.Navbar
5376  * Bootstrap Sidebar class
5377  *
5378  * @cfg {Boolean} inverse is inverted color
5379  * 
5380  * @cfg {String} type (nav | pills | tabs)
5381  * @cfg {Boolean} arrangement stacked | justified
5382  * @cfg {String} align (left | right) alignment
5383  * 
5384  * @cfg {Boolean} main (true|false) main nav bar? default false
5385  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5386  * 
5387  * @cfg {String} tag (header|footer|nav|div) default is nav 
5388
5389  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5390  * 
5391  * 
5392  * @constructor
5393  * Create a new Sidebar
5394  * @param {Object} config The config object
5395  */
5396
5397
5398 Roo.bootstrap.NavSimplebar = function(config){
5399     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5400 };
5401
5402 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5403     
5404     inverse: false,
5405     
5406     type: false,
5407     arrangement: '',
5408     align : false,
5409     
5410     weight : 'light',
5411     
5412     main : false,
5413     
5414     
5415     tag : false,
5416     
5417     
5418     getAutoCreate : function(){
5419         
5420         
5421         var cfg = {
5422             tag : this.tag || 'div',
5423             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5424         };
5425         if (['light','white'].indexOf(this.weight) > -1) {
5426             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5427         }
5428         cfg.cls += ' bg-' + this.weight;
5429         
5430         if (this.inverse) {
5431             cfg.cls += ' navbar-inverse';
5432             
5433         }
5434         
5435         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5436         
5437         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5438             return cfg;
5439         }
5440         
5441         
5442     
5443         
5444         cfg.cn = [
5445             {
5446                 cls: 'nav nav-' + this.xtype,
5447                 tag : 'ul'
5448             }
5449         ];
5450         
5451          
5452         this.type = this.type || 'nav';
5453         if (['tabs','pills'].indexOf(this.type) != -1) {
5454             cfg.cn[0].cls += ' nav-' + this.type
5455         
5456         
5457         } else {
5458             if (this.type!=='nav') {
5459                 Roo.log('nav type must be nav/tabs/pills')
5460             }
5461             cfg.cn[0].cls += ' navbar-nav'
5462         }
5463         
5464         
5465         
5466         
5467         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5468             cfg.cn[0].cls += ' nav-' + this.arrangement;
5469         }
5470         
5471         
5472         if (this.align === 'right') {
5473             cfg.cn[0].cls += ' navbar-right';
5474         }
5475         
5476         
5477         
5478         
5479         return cfg;
5480     
5481         
5482     }
5483     
5484     
5485     
5486 });
5487
5488
5489
5490  
5491
5492  
5493        /*
5494  * - LGPL
5495  *
5496  * navbar
5497  * navbar-fixed-top
5498  * navbar-expand-md  fixed-top 
5499  */
5500
5501 /**
5502  * @class Roo.bootstrap.NavHeaderbar
5503  * @extends Roo.bootstrap.NavSimplebar
5504  * Bootstrap Sidebar class
5505  *
5506  * @cfg {String} brand what is brand
5507  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5508  * @cfg {String} brand_href href of the brand
5509  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5510  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5511  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5512  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5513  * 
5514  * @constructor
5515  * Create a new Sidebar
5516  * @param {Object} config The config object
5517  */
5518
5519
5520 Roo.bootstrap.NavHeaderbar = function(config){
5521     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5522       
5523 };
5524
5525 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5526     
5527     position: '',
5528     brand: '',
5529     brand_href: false,
5530     srButton : true,
5531     autohide : false,
5532     desktopCenter : false,
5533    
5534     
5535     getAutoCreate : function(){
5536         
5537         var   cfg = {
5538             tag: this.nav || 'nav',
5539             cls: 'navbar navbar-expand-md',
5540             role: 'navigation',
5541             cn: []
5542         };
5543         
5544         var cn = cfg.cn;
5545         if (this.desktopCenter) {
5546             cn.push({cls : 'container', cn : []});
5547             cn = cn[0].cn;
5548         }
5549         
5550         if(this.srButton){
5551             var btn = {
5552                 tag: 'button',
5553                 type: 'button',
5554                 cls: 'navbar-toggle navbar-toggler',
5555                 'data-toggle': 'collapse',
5556                 cn: [
5557                     {
5558                         tag: 'span',
5559                         cls: 'sr-only',
5560                         html: 'Toggle navigation'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar navbar-toggler-icon'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     },
5570                     {
5571                         tag: 'span',
5572                         cls: 'icon-bar'
5573                     }
5574                 ]
5575             };
5576             
5577             cn.push( Roo.bootstrap.version == 4 ? btn : {
5578                 tag: 'div',
5579                 cls: 'navbar-header',
5580                 cn: [
5581                     btn
5582                 ]
5583             });
5584         }
5585         
5586         cn.push({
5587             tag: 'div',
5588             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5589             cn : []
5590         });
5591         
5592         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5593         
5594         if (['light','white'].indexOf(this.weight) > -1) {
5595             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5596         }
5597         cfg.cls += ' bg-' + this.weight;
5598         
5599         
5600         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5601             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5602             
5603             // tag can override this..
5604             
5605             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5606         }
5607         
5608         if (this.brand !== '') {
5609             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5610             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5611                 tag: 'a',
5612                 href: this.brand_href ? this.brand_href : '#',
5613                 cls: 'navbar-brand',
5614                 cn: [
5615                 this.brand
5616                 ]
5617             });
5618         }
5619         
5620         if(this.main){
5621             cfg.cls += ' main-nav';
5622         }
5623         
5624         
5625         return cfg;
5626
5627         
5628     },
5629     getHeaderChildContainer : function()
5630     {
5631         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5632             return this.el.select('.navbar-header',true).first();
5633         }
5634         
5635         return this.getChildContainer();
5636     },
5637     
5638     getChildContainer : function()
5639     {
5640          
5641         return this.el.select('.roo-navbar-collapse',true).first();
5642          
5643         
5644     },
5645     
5646     initEvents : function()
5647     {
5648         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5649         
5650         if (this.autohide) {
5651             
5652             var prevScroll = 0;
5653             var ft = this.el;
5654             
5655             Roo.get(document).on('scroll',function(e) {
5656                 var ns = Roo.get(document).getScroll().top;
5657                 var os = prevScroll;
5658                 prevScroll = ns;
5659                 
5660                 if(ns > os){
5661                     ft.removeClass('slideDown');
5662                     ft.addClass('slideUp');
5663                     return;
5664                 }
5665                 ft.removeClass('slideUp');
5666                 ft.addClass('slideDown');
5667                  
5668               
5669           },this);
5670         }
5671     }    
5672     
5673 });
5674
5675
5676
5677  
5678
5679  /*
5680  * - LGPL
5681  *
5682  * navbar
5683  * 
5684  */
5685
5686 /**
5687  * @class Roo.bootstrap.NavSidebar
5688  * @extends Roo.bootstrap.Navbar
5689  * Bootstrap Sidebar class
5690  * 
5691  * @constructor
5692  * Create a new Sidebar
5693  * @param {Object} config The config object
5694  */
5695
5696
5697 Roo.bootstrap.NavSidebar = function(config){
5698     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5699 };
5700
5701 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5702     
5703     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5704     
5705     getAutoCreate : function(){
5706         
5707         
5708         return  {
5709             tag: 'div',
5710             cls: 'sidebar sidebar-nav'
5711         };
5712     
5713         
5714     }
5715     
5716     
5717     
5718 });
5719
5720
5721
5722  
5723
5724  /*
5725  * - LGPL
5726  *
5727  * nav group
5728  * 
5729  */
5730
5731 /**
5732  * @class Roo.bootstrap.NavGroup
5733  * @extends Roo.bootstrap.Component
5734  * Bootstrap NavGroup class
5735  * @cfg {String} align (left|right)
5736  * @cfg {Boolean} inverse
5737  * @cfg {String} type (nav|pills|tab) default nav
5738  * @cfg {String} navId - reference Id for navbar.
5739  * @cfg {Boolean} pilltype default true (turn to off to disable active toggle)
5740  * 
5741  * @constructor
5742  * Create a new nav group
5743  * @param {Object} config The config object
5744  */
5745
5746 Roo.bootstrap.NavGroup = function(config){
5747     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5748     this.navItems = [];
5749    
5750     Roo.bootstrap.NavGroup.register(this);
5751      this.addEvents({
5752         /**
5753              * @event changed
5754              * Fires when the active item changes
5755              * @param {Roo.bootstrap.NavGroup} this
5756              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5757              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5758          */
5759         'changed': true
5760      });
5761     
5762 };
5763
5764 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5765     
5766     align: '',
5767     inverse: false,
5768     form: false,
5769     type: 'nav',
5770     navId : '',
5771     // private
5772     pilltype : true,
5773     
5774     navItems : false, 
5775     
5776     getAutoCreate : function()
5777     {
5778         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5779         
5780         cfg = {
5781             tag : 'ul',
5782             cls: 'nav' 
5783         };
5784         if (Roo.bootstrap.version == 4) {
5785             if (['tabs','pills'].indexOf(this.type) != -1) {
5786                 cfg.cls += ' nav-' + this.type; 
5787             } else {
5788                 // trying to remove so header bar can right align top?
5789                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5790                     // do not use on header bar... 
5791                     cfg.cls += ' navbar-nav';
5792                 }
5793             }
5794             
5795         } else {
5796             if (['tabs','pills'].indexOf(this.type) != -1) {
5797                 cfg.cls += ' nav-' + this.type
5798             } else {
5799                 if (this.type !== 'nav') {
5800                     Roo.log('nav type must be nav/tabs/pills')
5801                 }
5802                 cfg.cls += ' navbar-nav'
5803             }
5804         }
5805         
5806         if (this.parent() && this.parent().sidebar) {
5807             cfg = {
5808                 tag: 'ul',
5809                 cls: 'dashboard-menu sidebar-menu'
5810             };
5811             
5812             return cfg;
5813         }
5814         
5815         if (this.form === true) {
5816             cfg = {
5817                 tag: 'form',
5818                 cls: 'navbar-form form-inline'
5819             };
5820             //nav navbar-right ml-md-auto
5821             if (this.align === 'right') {
5822                 cfg.cls += ' navbar-right ml-md-auto';
5823             } else {
5824                 cfg.cls += ' navbar-left';
5825             }
5826         }
5827         
5828         if (this.align === 'right') {
5829             cfg.cls += ' navbar-right ml-md-auto';
5830         } else {
5831             cfg.cls += ' mr-auto';
5832         }
5833         
5834         if (this.inverse) {
5835             cfg.cls += ' navbar-inverse';
5836             
5837         }
5838         
5839         
5840         return cfg;
5841     },
5842     /**
5843     * sets the active Navigation item
5844     * @param {Roo.bootstrap.NavItem} the new current navitem
5845     */
5846     setActiveItem : function(item)
5847     {
5848         var prev = false;
5849         Roo.each(this.navItems, function(v){
5850             if (v == item) {
5851                 return ;
5852             }
5853             if (v.isActive()) {
5854                 v.setActive(false, true);
5855                 prev = v;
5856                 
5857             }
5858             
5859         });
5860
5861         item.setActive(true, true);
5862         this.fireEvent('changed', this, item, prev);
5863         
5864         
5865     },
5866     /**
5867     * gets the active Navigation item
5868     * @return {Roo.bootstrap.NavItem} the current navitem
5869     */
5870     getActive : function()
5871     {
5872         
5873         var prev = false;
5874         Roo.each(this.navItems, function(v){
5875             
5876             if (v.isActive()) {
5877                 prev = v;
5878                 
5879             }
5880             
5881         });
5882         return prev;
5883     },
5884     
5885     indexOfNav : function()
5886     {
5887         
5888         var prev = false;
5889         Roo.each(this.navItems, function(v,i){
5890             
5891             if (v.isActive()) {
5892                 prev = i;
5893                 
5894             }
5895             
5896         });
5897         return prev;
5898     },
5899     /**
5900     * adds a Navigation item
5901     * @param {Roo.bootstrap.NavItem} the navitem to add
5902     */
5903     addItem : function(cfg)
5904     {
5905         if (this.form && Roo.bootstrap.version == 4) {
5906             cfg.tag = 'div';
5907         }
5908         var cn = new Roo.bootstrap.NavItem(cfg);
5909         this.register(cn);
5910         cn.parentId = this.id;
5911         cn.onRender(this.el, null);
5912         return cn;
5913     },
5914     /**
5915     * register a Navigation item
5916     * @param {Roo.bootstrap.NavItem} the navitem to add
5917     */
5918     register : function(item)
5919     {
5920         this.navItems.push( item);
5921         item.navId = this.navId;
5922     
5923     },
5924     
5925     /**
5926     * clear all the Navigation item
5927     */
5928    
5929     clearAll : function()
5930     {
5931         this.navItems = [];
5932         this.el.dom.innerHTML = '';
5933     },
5934     
5935     getNavItem: function(tabId)
5936     {
5937         var ret = false;
5938         Roo.each(this.navItems, function(e) {
5939             if (e.tabId == tabId) {
5940                ret =  e;
5941                return false;
5942             }
5943             return true;
5944             
5945         });
5946         return ret;
5947     },
5948     
5949     setActiveNext : function()
5950     {
5951         var i = this.indexOfNav(this.getActive());
5952         if (i > this.navItems.length) {
5953             return;
5954         }
5955         this.setActiveItem(this.navItems[i+1]);
5956     },
5957     setActivePrev : function()
5958     {
5959         var i = this.indexOfNav(this.getActive());
5960         if (i  < 1) {
5961             return;
5962         }
5963         this.setActiveItem(this.navItems[i-1]);
5964     },
5965     clearWasActive : function(except) {
5966         Roo.each(this.navItems, function(e) {
5967             if (e.tabId != except.tabId && e.was_active) {
5968                e.was_active = false;
5969                return false;
5970             }
5971             return true;
5972             
5973         });
5974     },
5975     getWasActive : function ()
5976     {
5977         var r = false;
5978         Roo.each(this.navItems, function(e) {
5979             if (e.was_active) {
5980                r = e;
5981                return false;
5982             }
5983             return true;
5984             
5985         });
5986         return r;
5987     }
5988     
5989     
5990 });
5991
5992  
5993 Roo.apply(Roo.bootstrap.NavGroup, {
5994     
5995     groups: {},
5996      /**
5997     * register a Navigation Group
5998     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5999     */
6000     register : function(navgrp)
6001     {
6002         this.groups[navgrp.navId] = navgrp;
6003         
6004     },
6005     /**
6006     * fetch a Navigation Group based on the navigation ID
6007     * @param {string} the navgroup to add
6008     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6009     */
6010     get: function(navId) {
6011         if (typeof(this.groups[navId]) == 'undefined') {
6012             return false;
6013             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6014         }
6015         return this.groups[navId] ;
6016     }
6017     
6018     
6019     
6020 });
6021
6022  /*
6023  * - LGPL
6024  *
6025  * row
6026  * 
6027  */
6028
6029 /**
6030  * @class Roo.bootstrap.NavItem
6031  * @extends Roo.bootstrap.Component
6032  * Bootstrap Navbar.NavItem class
6033  * @cfg {String} href  link to
6034  * @cfg {String} button_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default none
6035  * @cfg {Boolean} button_outline show and outlined button
6036  * @cfg {String} html content of button
6037  * @cfg {String} badge text inside badge
6038  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6039  * @cfg {String} glyphicon DEPRICATED - use fa
6040  * @cfg {String} icon DEPRICATED - use fa
6041  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6042  * @cfg {Boolean} active Is item active
6043  * @cfg {Boolean} disabled Is item disabled
6044  * @cfg {String} linkcls  Link Class
6045  * @cfg {Boolean} preventDefault (true | false) default false
6046  * @cfg {String} tabId the tab that this item activates.
6047  * @cfg {String} tagtype (a|span) render as a href or span?
6048  * @cfg {Boolean} animateRef (true|false) link to element default false  
6049   
6050  * @constructor
6051  * Create a new Navbar Item
6052  * @param {Object} config The config object
6053  */
6054 Roo.bootstrap.NavItem = function(config){
6055     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6056     this.addEvents({
6057         // raw events
6058         /**
6059          * @event click
6060          * The raw click event for the entire grid.
6061          * @param {Roo.EventObject} e
6062          */
6063         "click" : true,
6064          /**
6065             * @event changed
6066             * Fires when the active item active state changes
6067             * @param {Roo.bootstrap.NavItem} this
6068             * @param {boolean} state the new state
6069              
6070          */
6071         'changed': true,
6072         /**
6073             * @event scrollto
6074             * Fires when scroll to element
6075             * @param {Roo.bootstrap.NavItem} this
6076             * @param {Object} options
6077             * @param {Roo.EventObject} e
6078              
6079          */
6080         'scrollto': true
6081     });
6082    
6083 };
6084
6085 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6086     
6087     href: false,
6088     html: '',
6089     badge: '',
6090     icon: false,
6091     fa : false,
6092     glyphicon: false,
6093     active: false,
6094     preventDefault : false,
6095     tabId : false,
6096     tagtype : 'a',
6097     tag: 'li',
6098     disabled : false,
6099     animateRef : false,
6100     was_active : false,
6101     button_weight : '',
6102     button_outline : false,
6103     linkcls : '',
6104     navLink: false,
6105     
6106     getAutoCreate : function(){
6107          
6108         var cfg = {
6109             tag: this.tag,
6110             cls: 'nav-item'
6111         };
6112         
6113         cfg.cls =  typeof(cfg.cls) == 'undefined'  ? '' : cfg.cls;
6114         
6115         if (this.active) {
6116             cfg.cls +=  ' active' ;
6117         }
6118         if (this.disabled) {
6119             cfg.cls += ' disabled';
6120         }
6121         
6122         // BS4 only?
6123         if (this.button_weight.length) {
6124             cfg.tag = this.href ? 'a' : 'button';
6125             cfg.html = this.html || '';
6126             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6127             if (this.href) {
6128                 cfg.href = this.href;
6129             }
6130             if (this.fa) {
6131                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6132             }
6133             
6134             // menu .. should add dropdown-menu class - so no need for carat..
6135             
6136             if (this.badge !== '') {
6137                  
6138                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6139             }
6140             return cfg;
6141         }
6142         
6143         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6144             cfg.cn = [
6145                 {
6146                     tag: this.tagtype,
6147                     href : this.href || "#",
6148                     html: this.html || ''
6149                 }
6150             ];
6151             if (this.tagtype == 'a') {
6152                 cfg.cn[0].cls = 'nav-link' +  (this.active ?  ' active'  : '') + ' ' + this.linkcls;
6153         
6154             }
6155             if (this.icon) {
6156                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6157             }
6158             if (this.fa) {
6159                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6160             }
6161             if(this.glyphicon) {
6162                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6163             }
6164             
6165             if (this.menu) {
6166                 
6167                 cfg.cn[0].html += " <span class='caret'></span>";
6168              
6169             }
6170             
6171             if (this.badge !== '') {
6172                  
6173                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6174             }
6175         }
6176         
6177         
6178         
6179         return cfg;
6180     },
6181     onRender : function(ct, position)
6182     {
6183        // Roo.log("Call onRender: " + this.xtype);
6184         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6185             this.tag = 'div';
6186         }
6187         
6188         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6189         this.navLink = this.el.select('.nav-link',true).first();
6190         return ret;
6191     },
6192       
6193     
6194     initEvents: function() 
6195     {
6196         if (typeof (this.menu) != 'undefined') {
6197             this.menu.parentType = this.xtype;
6198             this.menu.triggerEl = this.el;
6199             this.menu = this.addxtype(Roo.apply({}, this.menu));
6200         }
6201         
6202         this.el.on('click', this.onClick, this);
6203         
6204         //if(this.tagtype == 'span'){
6205         //    this.el.select('span',true).on('click', this.onClick, this);
6206         //}
6207        
6208         // at this point parent should be available..
6209         this.parent().register(this);
6210     },
6211     
6212     onClick : function(e)
6213     {
6214         if (e.getTarget('.dropdown-menu-item')) {
6215             // did you click on a menu itemm.... - then don't trigger onclick..
6216             return;
6217         }
6218         
6219         if(
6220                 this.preventDefault || 
6221                 this.href == '#' 
6222         ){
6223             Roo.log("NavItem - prevent Default?");
6224             e.preventDefault();
6225         }
6226         
6227         if (this.disabled) {
6228             return;
6229         }
6230         
6231         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6232         if (tg && tg.transition) {
6233             Roo.log("waiting for the transitionend");
6234             return;
6235         }
6236         
6237         
6238         
6239         //Roo.log("fire event clicked");
6240         if(this.fireEvent('click', this, e) === false){
6241             return;
6242         };
6243         
6244         if(this.tagtype == 'span'){
6245             return;
6246         }
6247         
6248         //Roo.log(this.href);
6249         var ael = this.el.select('a',true).first();
6250         //Roo.log(ael);
6251         
6252         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6253             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6254             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6255                 return; // ignore... - it's a 'hash' to another page.
6256             }
6257             Roo.log("NavItem - prevent Default?");
6258             e.preventDefault();
6259             this.scrollToElement(e);
6260         }
6261         
6262         
6263         var p =  this.parent();
6264    
6265         if (['tabs','pills'].indexOf(p.type)!==-1 && p.pilltype) {
6266             if (typeof(p.setActiveItem) !== 'undefined') {
6267                 p.setActiveItem(this);
6268             }
6269         }
6270         
6271         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6272         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6273             // remove the collapsed menu expand...
6274             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6275         }
6276     },
6277     
6278     isActive: function () {
6279         return this.active
6280     },
6281     setActive : function(state, fire, is_was_active)
6282     {
6283         if (this.active && !state && this.navId) {
6284             this.was_active = true;
6285             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6286             if (nv) {
6287                 nv.clearWasActive(this);
6288             }
6289             
6290         }
6291         this.active = state;
6292         
6293         if (!state ) {
6294             this.el.removeClass('active');
6295             this.navLink ? this.navLink.removeClass('active') : false;
6296         } else if (!this.el.hasClass('active')) {
6297             
6298             this.el.addClass('active');
6299             if (Roo.bootstrap.version == 4 && this.navLink ) {
6300                 this.navLink.addClass('active');
6301             }
6302             
6303         }
6304         if (fire) {
6305             this.fireEvent('changed', this, state);
6306         }
6307         
6308         // show a panel if it's registered and related..
6309         
6310         if (!this.navId || !this.tabId || !state || is_was_active) {
6311             return;
6312         }
6313         
6314         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6315         if (!tg) {
6316             return;
6317         }
6318         var pan = tg.getPanelByName(this.tabId);
6319         if (!pan) {
6320             return;
6321         }
6322         // if we can not flip to new panel - go back to old nav highlight..
6323         if (false == tg.showPanel(pan)) {
6324             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6325             if (nv) {
6326                 var onav = nv.getWasActive();
6327                 if (onav) {
6328                     onav.setActive(true, false, true);
6329                 }
6330             }
6331             
6332         }
6333         
6334         
6335         
6336     },
6337      // this should not be here...
6338     setDisabled : function(state)
6339     {
6340         this.disabled = state;
6341         if (!state ) {
6342             this.el.removeClass('disabled');
6343         } else if (!this.el.hasClass('disabled')) {
6344             this.el.addClass('disabled');
6345         }
6346         
6347     },
6348     
6349     /**
6350      * Fetch the element to display the tooltip on.
6351      * @return {Roo.Element} defaults to this.el
6352      */
6353     tooltipEl : function()
6354     {
6355         return this.el; //this.tagtype  == 'a' ? this.el  : this.el.select('' + this.tagtype + '', true).first();
6356     },
6357     
6358     scrollToElement : function(e)
6359     {
6360         var c = document.body;
6361         
6362         /*
6363          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6364          */
6365         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6366             c = document.documentElement;
6367         }
6368         
6369         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6370         
6371         if(!target){
6372             return;
6373         }
6374
6375         var o = target.calcOffsetsTo(c);
6376         
6377         var options = {
6378             target : target,
6379             value : o[1]
6380         };
6381         
6382         this.fireEvent('scrollto', this, options, e);
6383         
6384         Roo.get(c).scrollTo('top', options.value, true);
6385         
6386         return;
6387     }
6388 });
6389  
6390
6391  /*
6392  * - LGPL
6393  *
6394  * sidebar item
6395  *
6396  *  li
6397  *    <span> icon </span>
6398  *    <span> text </span>
6399  *    <span>badge </span>
6400  */
6401
6402 /**
6403  * @class Roo.bootstrap.NavSidebarItem
6404  * @extends Roo.bootstrap.NavItem
6405  * Bootstrap Navbar.NavSidebarItem class
6406  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6407  * {Boolean} open is the menu open
6408  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6409  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6410  * {String} buttonSize (sm|md|lg)the extra classes for the button
6411  * {Boolean} showArrow show arrow next to the text (default true)
6412  * @constructor
6413  * Create a new Navbar Button
6414  * @param {Object} config The config object
6415  */
6416 Roo.bootstrap.NavSidebarItem = function(config){
6417     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6418     this.addEvents({
6419         // raw events
6420         /**
6421          * @event click
6422          * The raw click event for the entire grid.
6423          * @param {Roo.EventObject} e
6424          */
6425         "click" : true,
6426          /**
6427             * @event changed
6428             * Fires when the active item active state changes
6429             * @param {Roo.bootstrap.NavSidebarItem} this
6430             * @param {boolean} state the new state
6431              
6432          */
6433         'changed': true
6434     });
6435    
6436 };
6437
6438 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6439     
6440     badgeWeight : 'default',
6441     
6442     open: false,
6443     
6444     buttonView : false,
6445     
6446     buttonWeight : 'default',
6447     
6448     buttonSize : 'md',
6449     
6450     showArrow : true,
6451     
6452     getAutoCreate : function(){
6453         
6454         
6455         var a = {
6456                 tag: 'a',
6457                 href : this.href || '#',
6458                 cls: '',
6459                 html : '',
6460                 cn : []
6461         };
6462         
6463         if(this.buttonView){
6464             a = {
6465                 tag: 'button',
6466                 href : this.href || '#',
6467                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6468                 html : this.html,
6469                 cn : []
6470             };
6471         }
6472         
6473         var cfg = {
6474             tag: 'li',
6475             cls: '',
6476             cn: [ a ]
6477         };
6478         
6479         if (this.active) {
6480             cfg.cls += ' active';
6481         }
6482         
6483         if (this.disabled) {
6484             cfg.cls += ' disabled';
6485         }
6486         if (this.open) {
6487             cfg.cls += ' open x-open';
6488         }
6489         // left icon..
6490         if (this.glyphicon || this.icon) {
6491             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6492             a.cn.push({ tag : 'i', cls : c }) ;
6493         }
6494         
6495         if(!this.buttonView){
6496             var span = {
6497                 tag: 'span',
6498                 html : this.html || ''
6499             };
6500
6501             a.cn.push(span);
6502             
6503         }
6504         
6505         if (this.badge !== '') {
6506             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6507         }
6508         
6509         if (this.menu) {
6510             
6511             if(this.showArrow){
6512                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6513             }
6514             
6515             a.cls += ' dropdown-toggle treeview' ;
6516         }
6517         
6518         return cfg;
6519     },
6520     
6521     initEvents : function()
6522     { 
6523         if (typeof (this.menu) != 'undefined') {
6524             this.menu.parentType = this.xtype;
6525             this.menu.triggerEl = this.el;
6526             this.menu = this.addxtype(Roo.apply({}, this.menu));
6527         }
6528         
6529         this.el.on('click', this.onClick, this);
6530         
6531         if(this.badge !== ''){
6532             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6533         }
6534         
6535     },
6536     
6537     onClick : function(e)
6538     {
6539         if(this.disabled){
6540             e.preventDefault();
6541             return;
6542         }
6543         
6544         if(this.preventDefault){
6545             e.preventDefault();
6546         }
6547         
6548         this.fireEvent('click', this, e);
6549     },
6550     
6551     disable : function()
6552     {
6553         this.setDisabled(true);
6554     },
6555     
6556     enable : function()
6557     {
6558         this.setDisabled(false);
6559     },
6560     
6561     setDisabled : function(state)
6562     {
6563         if(this.disabled == state){
6564             return;
6565         }
6566         
6567         this.disabled = state;
6568         
6569         if (state) {
6570             this.el.addClass('disabled');
6571             return;
6572         }
6573         
6574         this.el.removeClass('disabled');
6575         
6576         return;
6577     },
6578     
6579     setActive : function(state)
6580     {
6581         if(this.active == state){
6582             return;
6583         }
6584         
6585         this.active = state;
6586         
6587         if (state) {
6588             this.el.addClass('active');
6589             return;
6590         }
6591         
6592         this.el.removeClass('active');
6593         
6594         return;
6595     },
6596     
6597     isActive: function () 
6598     {
6599         return this.active;
6600     },
6601     
6602     setBadge : function(str)
6603     {
6604         if(!this.badgeEl){
6605             return;
6606         }
6607         
6608         this.badgeEl.dom.innerHTML = str;
6609     }
6610     
6611    
6612      
6613  
6614 });
6615  
6616
6617  /*
6618  * - LGPL
6619  *
6620  *  Breadcrumb Nav
6621  * 
6622  */
6623 Roo.namespace('Roo.bootstrap.breadcrumb');
6624
6625
6626 /**
6627  * @class Roo.bootstrap.breadcrumb.Nav
6628  * @extends Roo.bootstrap.Component
6629  * Bootstrap Breadcrumb Nav Class
6630  *  
6631  * @children Roo.bootstrap.breadcrumb.Item
6632  * 
6633  * @constructor
6634  * Create a new breadcrumb.Nav
6635  * @param {Object} config The config object
6636  */
6637
6638
6639 Roo.bootstrap.breadcrumb.Nav = function(config){
6640     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6641     
6642     
6643 };
6644
6645 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6646     
6647     getAutoCreate : function()
6648     {
6649
6650         var cfg = {
6651             tag: 'nav',
6652             cn : [
6653                 {
6654                     tag : 'ol',
6655                     cls : 'breadcrumb'
6656                 }
6657             ]
6658             
6659         };
6660           
6661         return cfg;
6662     },
6663     
6664     initEvents: function()
6665     {
6666         this.olEl = this.el.select('ol',true).first();    
6667     },
6668     getChildContainer : function()
6669     {
6670         return this.olEl;  
6671     }
6672     
6673 });
6674
6675  /*
6676  * - LGPL
6677  *
6678  *  Breadcrumb Item
6679  * 
6680  */
6681
6682
6683 /**
6684  * @class Roo.bootstrap.breadcrumb.Nav
6685  * @extends Roo.bootstrap.Component
6686  * Bootstrap Breadcrumb Nav Class
6687  *  
6688  * @children Roo.bootstrap.breadcrumb.Component
6689  * @cfg {String} html the content of the link.
6690  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6691  * @cfg {Boolean} active is it active
6692
6693  * 
6694  * @constructor
6695  * Create a new breadcrumb.Nav
6696  * @param {Object} config The config object
6697  */
6698
6699 Roo.bootstrap.breadcrumb.Item = function(config){
6700     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6701     this.addEvents({
6702         // img events
6703         /**
6704          * @event click
6705          * The img click event for the img.
6706          * @param {Roo.EventObject} e
6707          */
6708         "click" : true
6709     });
6710     
6711 };
6712
6713 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6714     
6715     href: false,
6716     html : '',
6717     
6718     getAutoCreate : function()
6719     {
6720
6721         var cfg = {
6722             tag: 'li',
6723             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6724         };
6725         if (this.href !== false) {
6726             cfg.cn = [{
6727                 tag : 'a',
6728                 href : this.href,
6729                 html : this.html
6730             }];
6731         } else {
6732             cfg.html = this.html;
6733         }
6734         
6735         return cfg;
6736     },
6737     
6738     initEvents: function()
6739     {
6740         if (this.href) {
6741             this.el.select('a', true).first().on('click',this.onClick, this)
6742         }
6743         
6744     },
6745     onClick : function(e)
6746     {
6747         e.preventDefault();
6748         this.fireEvent('click',this,  e);
6749     }
6750     
6751 });
6752
6753  /*
6754  * - LGPL
6755  *
6756  * row
6757  * 
6758  */
6759
6760 /**
6761  * @class Roo.bootstrap.Row
6762  * @extends Roo.bootstrap.Component
6763  * Bootstrap Row class (contains columns...)
6764  * 
6765  * @constructor
6766  * Create a new Row
6767  * @param {Object} config The config object
6768  */
6769
6770 Roo.bootstrap.Row = function(config){
6771     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6772 };
6773
6774 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6775     
6776     getAutoCreate : function(){
6777        return {
6778             cls: 'row clearfix'
6779        };
6780     }
6781     
6782     
6783 });
6784
6785  
6786
6787  /*
6788  * - LGPL
6789  *
6790  * pagination
6791  * 
6792  */
6793
6794 /**
6795  * @class Roo.bootstrap.Pagination
6796  * @extends Roo.bootstrap.Component
6797  * Bootstrap Pagination class
6798  * @cfg {String} size xs | sm | md | lg
6799  * @cfg {Boolean} inverse false | true
6800  * 
6801  * @constructor
6802  * Create a new Pagination
6803  * @param {Object} config The config object
6804  */
6805
6806 Roo.bootstrap.Pagination = function(config){
6807     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6808 };
6809
6810 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6811     
6812     cls: false,
6813     size: false,
6814     inverse: false,
6815     
6816     getAutoCreate : function(){
6817         var cfg = {
6818             tag: 'ul',
6819                 cls: 'pagination'
6820         };
6821         if (this.inverse) {
6822             cfg.cls += ' inverse';
6823         }
6824         if (this.html) {
6825             cfg.html=this.html;
6826         }
6827         if (this.cls) {
6828             cfg.cls += " " + this.cls;
6829         }
6830         return cfg;
6831     }
6832    
6833 });
6834
6835  
6836
6837  /*
6838  * - LGPL
6839  *
6840  * Pagination item
6841  * 
6842  */
6843
6844
6845 /**
6846  * @class Roo.bootstrap.PaginationItem
6847  * @extends Roo.bootstrap.Component
6848  * Bootstrap PaginationItem class
6849  * @cfg {String} html text
6850  * @cfg {String} href the link
6851  * @cfg {Boolean} preventDefault (true | false) default true
6852  * @cfg {Boolean} active (true | false) default false
6853  * @cfg {Boolean} disabled default false
6854  * 
6855  * 
6856  * @constructor
6857  * Create a new PaginationItem
6858  * @param {Object} config The config object
6859  */
6860
6861
6862 Roo.bootstrap.PaginationItem = function(config){
6863     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6864     this.addEvents({
6865         // raw events
6866         /**
6867          * @event click
6868          * The raw click event for the entire grid.
6869          * @param {Roo.EventObject} e
6870          */
6871         "click" : true
6872     });
6873 };
6874
6875 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6876     
6877     href : false,
6878     html : false,
6879     preventDefault: true,
6880     active : false,
6881     cls : false,
6882     disabled: false,
6883     
6884     getAutoCreate : function(){
6885         var cfg= {
6886             tag: 'li',
6887             cn: [
6888                 {
6889                     tag : 'a',
6890                     href : this.href ? this.href : '#',
6891                     html : this.html ? this.html : ''
6892                 }
6893             ]
6894         };
6895         
6896         if(this.cls){
6897             cfg.cls = this.cls;
6898         }
6899         
6900         if(this.disabled){
6901             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6902         }
6903         
6904         if(this.active){
6905             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6906         }
6907         
6908         return cfg;
6909     },
6910     
6911     initEvents: function() {
6912         
6913         this.el.on('click', this.onClick, this);
6914         
6915     },
6916     onClick : function(e)
6917     {
6918         Roo.log('PaginationItem on click ');
6919         if(this.preventDefault){
6920             e.preventDefault();
6921         }
6922         
6923         if(this.disabled){
6924             return;
6925         }
6926         
6927         this.fireEvent('click', this, e);
6928     }
6929    
6930 });
6931
6932  
6933
6934  /*
6935  * - LGPL
6936  *
6937  * slider
6938  * 
6939  */
6940
6941
6942 /**
6943  * @class Roo.bootstrap.Slider
6944  * @extends Roo.bootstrap.Component
6945  * Bootstrap Slider class
6946  *    
6947  * @constructor
6948  * Create a new Slider
6949  * @param {Object} config The config object
6950  */
6951
6952 Roo.bootstrap.Slider = function(config){
6953     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6954 };
6955
6956 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6957     
6958     getAutoCreate : function(){
6959         
6960         var cfg = {
6961             tag: 'div',
6962             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6963             cn: [
6964                 {
6965                     tag: 'a',
6966                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6967                 }
6968             ]
6969         };
6970         
6971         return cfg;
6972     }
6973    
6974 });
6975
6976  /*
6977  * Based on:
6978  * Ext JS Library 1.1.1
6979  * Copyright(c) 2006-2007, Ext JS, LLC.
6980  *
6981  * Originally Released Under LGPL - original licence link has changed is not relivant.
6982  *
6983  * Fork - LGPL
6984  * <script type="text/javascript">
6985  */
6986  
6987
6988 /**
6989  * @class Roo.grid.ColumnModel
6990  * @extends Roo.util.Observable
6991  * This is the default implementation of a ColumnModel used by the Grid. It defines
6992  * the columns in the grid.
6993  * <br>Usage:<br>
6994  <pre><code>
6995  var colModel = new Roo.grid.ColumnModel([
6996         {header: "Ticker", width: 60, sortable: true, locked: true},
6997         {header: "Company Name", width: 150, sortable: true},
6998         {header: "Market Cap.", width: 100, sortable: true},
6999         {header: "$ Sales", width: 100, sortable: true, renderer: money},
7000         {header: "Employees", width: 100, sortable: true, resizable: false}
7001  ]);
7002  </code></pre>
7003  * <p>
7004  
7005  * The config options listed for this class are options which may appear in each
7006  * individual column definition.
7007  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7008  * @constructor
7009  * @param {Object} config An Array of column config objects. See this class's
7010  * config objects for details.
7011 */
7012 Roo.grid.ColumnModel = function(config){
7013         /**
7014      * The config passed into the constructor
7015      */
7016     this.config = config;
7017     this.lookup = {};
7018
7019     // if no id, create one
7020     // if the column does not have a dataIndex mapping,
7021     // map it to the order it is in the config
7022     for(var i = 0, len = config.length; i < len; i++){
7023         var c = config[i];
7024         if(typeof c.dataIndex == "undefined"){
7025             c.dataIndex = i;
7026         }
7027         if(typeof c.renderer == "string"){
7028             c.renderer = Roo.util.Format[c.renderer];
7029         }
7030         if(typeof c.id == "undefined"){
7031             c.id = Roo.id();
7032         }
7033         if(c.editor && c.editor.xtype){
7034             c.editor  = Roo.factory(c.editor, Roo.grid);
7035         }
7036         if(c.editor && c.editor.isFormField){
7037             c.editor = new Roo.grid.GridEditor(c.editor);
7038         }
7039         this.lookup[c.id] = c;
7040     }
7041
7042     /**
7043      * The width of columns which have no width specified (defaults to 100)
7044      * @type Number
7045      */
7046     this.defaultWidth = 100;
7047
7048     /**
7049      * Default sortable of columns which have no sortable specified (defaults to false)
7050      * @type Boolean
7051      */
7052     this.defaultSortable = false;
7053
7054     this.addEvents({
7055         /**
7056              * @event widthchange
7057              * Fires when the width of a column changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newWidth The new width
7061              */
7062             "widthchange": true,
7063         /**
7064              * @event headerchange
7065              * Fires when the text of a header changes.
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Number} newText The new header text
7069              */
7070             "headerchange": true,
7071         /**
7072              * @event hiddenchange
7073              * Fires when a column is hidden or "unhidden".
7074              * @param {ColumnModel} this
7075              * @param {Number} columnIndex The column index
7076              * @param {Boolean} hidden true if hidden, false otherwise
7077              */
7078             "hiddenchange": true,
7079             /**
7080          * @event columnmoved
7081          * Fires when a column is moved.
7082          * @param {ColumnModel} this
7083          * @param {Number} oldIndex
7084          * @param {Number} newIndex
7085          */
7086         "columnmoved" : true,
7087         /**
7088          * @event columlockchange
7089          * Fires when a column's locked state is changed
7090          * @param {ColumnModel} this
7091          * @param {Number} colIndex
7092          * @param {Boolean} locked true if locked
7093          */
7094         "columnlockchange" : true
7095     });
7096     Roo.grid.ColumnModel.superclass.constructor.call(this);
7097 };
7098 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7099     /**
7100      * @cfg {String} header The header text to display in the Grid view.
7101      */
7102     /**
7103      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7104      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7105      * specified, the column's index is used as an index into the Record's data Array.
7106      */
7107     /**
7108      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7109      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7110      */
7111     /**
7112      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7113      * Defaults to the value of the {@link #defaultSortable} property.
7114      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7115      */
7116     /**
7117      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7118      */
7119     /**
7120      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7121      */
7122     /**
7123      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7124      */
7125     /**
7126      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7127      */
7128     /**
7129      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7130      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7131      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7132      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7133      */
7134        /**
7135      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7136      */
7137     /**
7138      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7139      */
7140     /**
7141      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7142      */
7143     /**
7144      * @cfg {String} cursor (Optional)
7145      */
7146     /**
7147      * @cfg {String} tooltip (Optional)
7148      */
7149     /**
7150      * @cfg {Number} xs (Optional)
7151      */
7152     /**
7153      * @cfg {Number} sm (Optional)
7154      */
7155     /**
7156      * @cfg {Number} md (Optional)
7157      */
7158     /**
7159      * @cfg {Number} lg (Optional)
7160      */
7161     /**
7162      * Returns the id of the column at the specified index.
7163      * @param {Number} index The column index
7164      * @return {String} the id
7165      */
7166     getColumnId : function(index){
7167         return this.config[index].id;
7168     },
7169
7170     /**
7171      * Returns the column for a specified id.
7172      * @param {String} id The column id
7173      * @return {Object} the column
7174      */
7175     getColumnById : function(id){
7176         return this.lookup[id];
7177     },
7178
7179     
7180     /**
7181      * Returns the column for a specified dataIndex.
7182      * @param {String} dataIndex The column dataIndex
7183      * @return {Object|Boolean} the column or false if not found
7184      */
7185     getColumnByDataIndex: function(dataIndex){
7186         var index = this.findColumnIndex(dataIndex);
7187         return index > -1 ? this.config[index] : false;
7188     },
7189     
7190     /**
7191      * Returns the index for a specified column id.
7192      * @param {String} id The column id
7193      * @return {Number} the index, or -1 if not found
7194      */
7195     getIndexById : function(id){
7196         for(var i = 0, len = this.config.length; i < len; i++){
7197             if(this.config[i].id == id){
7198                 return i;
7199             }
7200         }
7201         return -1;
7202     },
7203     
7204     /**
7205      * Returns the index for a specified column dataIndex.
7206      * @param {String} dataIndex The column dataIndex
7207      * @return {Number} the index, or -1 if not found
7208      */
7209     
7210     findColumnIndex : function(dataIndex){
7211         for(var i = 0, len = this.config.length; i < len; i++){
7212             if(this.config[i].dataIndex == dataIndex){
7213                 return i;
7214             }
7215         }
7216         return -1;
7217     },
7218     
7219     
7220     moveColumn : function(oldIndex, newIndex){
7221         var c = this.config[oldIndex];
7222         this.config.splice(oldIndex, 1);
7223         this.config.splice(newIndex, 0, c);
7224         this.dataMap = null;
7225         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7226     },
7227
7228     isLocked : function(colIndex){
7229         return this.config[colIndex].locked === true;
7230     },
7231
7232     setLocked : function(colIndex, value, suppressEvent){
7233         if(this.isLocked(colIndex) == value){
7234             return;
7235         }
7236         this.config[colIndex].locked = value;
7237         if(!suppressEvent){
7238             this.fireEvent("columnlockchange", this, colIndex, value);
7239         }
7240     },
7241
7242     getTotalLockedWidth : function(){
7243         var totalWidth = 0;
7244         for(var i = 0; i < this.config.length; i++){
7245             if(this.isLocked(i) && !this.isHidden(i)){
7246                 this.totalWidth += this.getColumnWidth(i);
7247             }
7248         }
7249         return totalWidth;
7250     },
7251
7252     getLockedCount : function(){
7253         for(var i = 0, len = this.config.length; i < len; i++){
7254             if(!this.isLocked(i)){
7255                 return i;
7256             }
7257         }
7258         
7259         return this.config.length;
7260     },
7261
7262     /**
7263      * Returns the number of columns.
7264      * @return {Number}
7265      */
7266     getColumnCount : function(visibleOnly){
7267         if(visibleOnly === true){
7268             var c = 0;
7269             for(var i = 0, len = this.config.length; i < len; i++){
7270                 if(!this.isHidden(i)){
7271                     c++;
7272                 }
7273             }
7274             return c;
7275         }
7276         return this.config.length;
7277     },
7278
7279     /**
7280      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7281      * @param {Function} fn
7282      * @param {Object} scope (optional)
7283      * @return {Array} result
7284      */
7285     getColumnsBy : function(fn, scope){
7286         var r = [];
7287         for(var i = 0, len = this.config.length; i < len; i++){
7288             var c = this.config[i];
7289             if(fn.call(scope||this, c, i) === true){
7290                 r[r.length] = c;
7291             }
7292         }
7293         return r;
7294     },
7295
7296     /**
7297      * Returns true if the specified column is sortable.
7298      * @param {Number} col The column index
7299      * @return {Boolean}
7300      */
7301     isSortable : function(col){
7302         if(typeof this.config[col].sortable == "undefined"){
7303             return this.defaultSortable;
7304         }
7305         return this.config[col].sortable;
7306     },
7307
7308     /**
7309      * Returns the rendering (formatting) function defined for the column.
7310      * @param {Number} col The column index.
7311      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7312      */
7313     getRenderer : function(col){
7314         if(!this.config[col].renderer){
7315             return Roo.grid.ColumnModel.defaultRenderer;
7316         }
7317         return this.config[col].renderer;
7318     },
7319
7320     /**
7321      * Sets the rendering (formatting) function for a column.
7322      * @param {Number} col The column index
7323      * @param {Function} fn The function to use to process the cell's raw data
7324      * to return HTML markup for the grid view. The render function is called with
7325      * the following parameters:<ul>
7326      * <li>Data value.</li>
7327      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7328      * <li>css A CSS style string to apply to the table cell.</li>
7329      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7330      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7331      * <li>Row index</li>
7332      * <li>Column index</li>
7333      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7334      */
7335     setRenderer : function(col, fn){
7336         this.config[col].renderer = fn;
7337     },
7338
7339     /**
7340      * Returns the width for the specified column.
7341      * @param {Number} col The column index
7342      * @return {Number}
7343      */
7344     getColumnWidth : function(col){
7345         return this.config[col].width * 1 || this.defaultWidth;
7346     },
7347
7348     /**
7349      * Sets the width for a column.
7350      * @param {Number} col The column index
7351      * @param {Number} width The new width
7352      */
7353     setColumnWidth : function(col, width, suppressEvent){
7354         this.config[col].width = width;
7355         this.totalWidth = null;
7356         if(!suppressEvent){
7357              this.fireEvent("widthchange", this, col, width);
7358         }
7359     },
7360
7361     /**
7362      * Returns the total width of all columns.
7363      * @param {Boolean} includeHidden True to include hidden column widths
7364      * @return {Number}
7365      */
7366     getTotalWidth : function(includeHidden){
7367         if(!this.totalWidth){
7368             this.totalWidth = 0;
7369             for(var i = 0, len = this.config.length; i < len; i++){
7370                 if(includeHidden || !this.isHidden(i)){
7371                     this.totalWidth += this.getColumnWidth(i);
7372                 }
7373             }
7374         }
7375         return this.totalWidth;
7376     },
7377
7378     /**
7379      * Returns the header for the specified column.
7380      * @param {Number} col The column index
7381      * @return {String}
7382      */
7383     getColumnHeader : function(col){
7384         return this.config[col].header;
7385     },
7386
7387     /**
7388      * Sets the header for a column.
7389      * @param {Number} col The column index
7390      * @param {String} header The new header
7391      */
7392     setColumnHeader : function(col, header){
7393         this.config[col].header = header;
7394         this.fireEvent("headerchange", this, col, header);
7395     },
7396
7397     /**
7398      * Returns the tooltip for the specified column.
7399      * @param {Number} col The column index
7400      * @return {String}
7401      */
7402     getColumnTooltip : function(col){
7403             return this.config[col].tooltip;
7404     },
7405     /**
7406      * Sets the tooltip for a column.
7407      * @param {Number} col The column index
7408      * @param {String} tooltip The new tooltip
7409      */
7410     setColumnTooltip : function(col, tooltip){
7411             this.config[col].tooltip = tooltip;
7412     },
7413
7414     /**
7415      * Returns the dataIndex for the specified column.
7416      * @param {Number} col The column index
7417      * @return {Number}
7418      */
7419     getDataIndex : function(col){
7420         return this.config[col].dataIndex;
7421     },
7422
7423     /**
7424      * Sets the dataIndex for a column.
7425      * @param {Number} col The column index
7426      * @param {Number} dataIndex The new dataIndex
7427      */
7428     setDataIndex : function(col, dataIndex){
7429         this.config[col].dataIndex = dataIndex;
7430     },
7431
7432     
7433     
7434     /**
7435      * Returns true if the cell is editable.
7436      * @param {Number} colIndex The column index
7437      * @param {Number} rowIndex The row index - this is nto actually used..?
7438      * @return {Boolean}
7439      */
7440     isCellEditable : function(colIndex, rowIndex){
7441         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7442     },
7443
7444     /**
7445      * Returns the editor defined for the cell/column.
7446      * return false or null to disable editing.
7447      * @param {Number} colIndex The column index
7448      * @param {Number} rowIndex The row index
7449      * @return {Object}
7450      */
7451     getCellEditor : function(colIndex, rowIndex){
7452         return this.config[colIndex].editor;
7453     },
7454
7455     /**
7456      * Sets if a column is editable.
7457      * @param {Number} col The column index
7458      * @param {Boolean} editable True if the column is editable
7459      */
7460     setEditable : function(col, editable){
7461         this.config[col].editable = editable;
7462     },
7463
7464
7465     /**
7466      * Returns true if the column is hidden.
7467      * @param {Number} colIndex The column index
7468      * @return {Boolean}
7469      */
7470     isHidden : function(colIndex){
7471         return this.config[colIndex].hidden;
7472     },
7473
7474
7475     /**
7476      * Returns true if the column width cannot be changed
7477      */
7478     isFixed : function(colIndex){
7479         return this.config[colIndex].fixed;
7480     },
7481
7482     /**
7483      * Returns true if the column can be resized
7484      * @return {Boolean}
7485      */
7486     isResizable : function(colIndex){
7487         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7488     },
7489     /**
7490      * Sets if a column is hidden.
7491      * @param {Number} colIndex The column index
7492      * @param {Boolean} hidden True if the column is hidden
7493      */
7494     setHidden : function(colIndex, hidden){
7495         this.config[colIndex].hidden = hidden;
7496         this.totalWidth = null;
7497         this.fireEvent("hiddenchange", this, colIndex, hidden);
7498     },
7499
7500     /**
7501      * Sets the editor for a column.
7502      * @param {Number} col The column index
7503      * @param {Object} editor The editor object
7504      */
7505     setEditor : function(col, editor){
7506         this.config[col].editor = editor;
7507     }
7508 });
7509
7510 Roo.grid.ColumnModel.defaultRenderer = function(value)
7511 {
7512     if(typeof value == "object") {
7513         return value;
7514     }
7515         if(typeof value == "string" && value.length < 1){
7516             return "&#160;";
7517         }
7518     
7519         return String.format("{0}", value);
7520 };
7521
7522 // Alias for backwards compatibility
7523 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7524 /*
7525  * Based on:
7526  * Ext JS Library 1.1.1
7527  * Copyright(c) 2006-2007, Ext JS, LLC.
7528  *
7529  * Originally Released Under LGPL - original licence link has changed is not relivant.
7530  *
7531  * Fork - LGPL
7532  * <script type="text/javascript">
7533  */
7534  
7535 /**
7536  * @class Roo.LoadMask
7537  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7538  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7539  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7540  * element's UpdateManager load indicator and will be destroyed after the initial load.
7541  * @constructor
7542  * Create a new LoadMask
7543  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7544  * @param {Object} config The config object
7545  */
7546 Roo.LoadMask = function(el, config){
7547     this.el = Roo.get(el);
7548     Roo.apply(this, config);
7549     if(this.store){
7550         this.store.on('beforeload', this.onBeforeLoad, this);
7551         this.store.on('load', this.onLoad, this);
7552         this.store.on('loadexception', this.onLoadException, this);
7553         this.removeMask = false;
7554     }else{
7555         var um = this.el.getUpdateManager();
7556         um.showLoadIndicator = false; // disable the default indicator
7557         um.on('beforeupdate', this.onBeforeLoad, this);
7558         um.on('update', this.onLoad, this);
7559         um.on('failure', this.onLoad, this);
7560         this.removeMask = true;
7561     }
7562 };
7563
7564 Roo.LoadMask.prototype = {
7565     /**
7566      * @cfg {Boolean} removeMask
7567      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7568      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7569      */
7570     /**
7571      * @cfg {String} msg
7572      * The text to display in a centered loading message box (defaults to 'Loading...')
7573      */
7574     msg : 'Loading...',
7575     /**
7576      * @cfg {String} msgCls
7577      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7578      */
7579     msgCls : 'x-mask-loading',
7580
7581     /**
7582      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7583      * @type Boolean
7584      */
7585     disabled: false,
7586
7587     /**
7588      * Disables the mask to prevent it from being displayed
7589      */
7590     disable : function(){
7591        this.disabled = true;
7592     },
7593
7594     /**
7595      * Enables the mask so that it can be displayed
7596      */
7597     enable : function(){
7598         this.disabled = false;
7599     },
7600     
7601     onLoadException : function()
7602     {
7603         Roo.log(arguments);
7604         
7605         if (typeof(arguments[3]) != 'undefined') {
7606             Roo.MessageBox.alert("Error loading",arguments[3]);
7607         } 
7608         /*
7609         try {
7610             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7611                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7612             }   
7613         } catch(e) {
7614             
7615         }
7616         */
7617     
7618         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7619     },
7620     // private
7621     onLoad : function()
7622     {
7623         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7624     },
7625
7626     // private
7627     onBeforeLoad : function(){
7628         if(!this.disabled){
7629             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7630         }
7631     },
7632
7633     // private
7634     destroy : function(){
7635         if(this.store){
7636             this.store.un('beforeload', this.onBeforeLoad, this);
7637             this.store.un('load', this.onLoad, this);
7638             this.store.un('loadexception', this.onLoadException, this);
7639         }else{
7640             var um = this.el.getUpdateManager();
7641             um.un('beforeupdate', this.onBeforeLoad, this);
7642             um.un('update', this.onLoad, this);
7643             um.un('failure', this.onLoad, this);
7644         }
7645     }
7646 };/*
7647  * - LGPL
7648  *
7649  * table
7650  * 
7651  */
7652
7653 /**
7654  * @class Roo.bootstrap.Table
7655  * @extends Roo.bootstrap.Component
7656  * Bootstrap Table class
7657  * @cfg {String} cls table class
7658  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7659  * @cfg {String} bgcolor Specifies the background color for a table
7660  * @cfg {Number} border Specifies whether the table cells should have borders or not
7661  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7662  * @cfg {Number} cellspacing Specifies the space between cells
7663  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7664  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7665  * @cfg {String} sortable Specifies that the table should be sortable
7666  * @cfg {String} summary Specifies a summary of the content of a table
7667  * @cfg {Number} width Specifies the width of a table
7668  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7669  * 
7670  * @cfg {boolean} striped Should the rows be alternative striped
7671  * @cfg {boolean} bordered Add borders to the table
7672  * @cfg {boolean} hover Add hover highlighting
7673  * @cfg {boolean} condensed Format condensed
7674  * @cfg {boolean} responsive Format condensed
7675  * @cfg {Boolean} loadMask (true|false) default false
7676  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7677  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7678  * @cfg {Boolean} rowSelection (true|false) default false
7679  * @cfg {Boolean} cellSelection (true|false) default false
7680  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7681  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7682  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7683  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7684  
7685  * 
7686  * @constructor
7687  * Create a new Table
7688  * @param {Object} config The config object
7689  */
7690
7691 Roo.bootstrap.Table = function(config){
7692     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7693     
7694   
7695     
7696     // BC...
7697     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7698     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7699     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7700     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7701     
7702     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7703     if (this.sm) {
7704         this.sm.grid = this;
7705         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7706         this.sm = this.selModel;
7707         this.sm.xmodule = this.xmodule || false;
7708     }
7709     
7710     if (this.cm && typeof(this.cm.config) == 'undefined') {
7711         this.colModel = new Roo.grid.ColumnModel(this.cm);
7712         this.cm = this.colModel;
7713         this.cm.xmodule = this.xmodule || false;
7714     }
7715     if (this.store) {
7716         this.store= Roo.factory(this.store, Roo.data);
7717         this.ds = this.store;
7718         this.ds.xmodule = this.xmodule || false;
7719          
7720     }
7721     if (this.footer && this.store) {
7722         this.footer.dataSource = this.ds;
7723         this.footer = Roo.factory(this.footer);
7724     }
7725     
7726     /** @private */
7727     this.addEvents({
7728         /**
7729          * @event cellclick
7730          * Fires when a cell is clicked
7731          * @param {Roo.bootstrap.Table} this
7732          * @param {Roo.Element} el
7733          * @param {Number} rowIndex
7734          * @param {Number} columnIndex
7735          * @param {Roo.EventObject} e
7736          */
7737         "cellclick" : true,
7738         /**
7739          * @event celldblclick
7740          * Fires when a cell is double clicked
7741          * @param {Roo.bootstrap.Table} this
7742          * @param {Roo.Element} el
7743          * @param {Number} rowIndex
7744          * @param {Number} columnIndex
7745          * @param {Roo.EventObject} e
7746          */
7747         "celldblclick" : true,
7748         /**
7749          * @event rowclick
7750          * Fires when a row is clicked
7751          * @param {Roo.bootstrap.Table} this
7752          * @param {Roo.Element} el
7753          * @param {Number} rowIndex
7754          * @param {Roo.EventObject} e
7755          */
7756         "rowclick" : true,
7757         /**
7758          * @event rowdblclick
7759          * Fires when a row is double clicked
7760          * @param {Roo.bootstrap.Table} this
7761          * @param {Roo.Element} el
7762          * @param {Number} rowIndex
7763          * @param {Roo.EventObject} e
7764          */
7765         "rowdblclick" : true,
7766         /**
7767          * @event mouseover
7768          * Fires when a mouseover occur
7769          * @param {Roo.bootstrap.Table} this
7770          * @param {Roo.Element} el
7771          * @param {Number} rowIndex
7772          * @param {Number} columnIndex
7773          * @param {Roo.EventObject} e
7774          */
7775         "mouseover" : true,
7776         /**
7777          * @event mouseout
7778          * Fires when a mouseout occur
7779          * @param {Roo.bootstrap.Table} this
7780          * @param {Roo.Element} el
7781          * @param {Number} rowIndex
7782          * @param {Number} columnIndex
7783          * @param {Roo.EventObject} e
7784          */
7785         "mouseout" : true,
7786         /**
7787          * @event rowclass
7788          * Fires when a row is rendered, so you can change add a style to it.
7789          * @param {Roo.bootstrap.Table} this
7790          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7791          */
7792         'rowclass' : true,
7793           /**
7794          * @event rowsrendered
7795          * Fires when all the  rows have been rendered
7796          * @param {Roo.bootstrap.Table} this
7797          */
7798         'rowsrendered' : true,
7799         /**
7800          * @event contextmenu
7801          * The raw contextmenu event for the entire grid.
7802          * @param {Roo.EventObject} e
7803          */
7804         "contextmenu" : true,
7805         /**
7806          * @event rowcontextmenu
7807          * Fires when a row is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Roo.EventObject} e
7811          */
7812         "rowcontextmenu" : true,
7813         /**
7814          * @event cellcontextmenu
7815          * Fires when a cell is right clicked
7816          * @param {Roo.bootstrap.Table} this
7817          * @param {Number} rowIndex
7818          * @param {Number} cellIndex
7819          * @param {Roo.EventObject} e
7820          */
7821          "cellcontextmenu" : true,
7822          /**
7823          * @event headercontextmenu
7824          * Fires when a header is right clicked
7825          * @param {Roo.bootstrap.Table} this
7826          * @param {Number} columnIndex
7827          * @param {Roo.EventObject} e
7828          */
7829         "headercontextmenu" : true
7830     });
7831 };
7832
7833 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7834     
7835     cls: false,
7836     align: false,
7837     bgcolor: false,
7838     border: false,
7839     cellpadding: false,
7840     cellspacing: false,
7841     frame: false,
7842     rules: false,
7843     sortable: false,
7844     summary: false,
7845     width: false,
7846     striped : false,
7847     scrollBody : false,
7848     bordered: false,
7849     hover:  false,
7850     condensed : false,
7851     responsive : false,
7852     sm : false,
7853     cm : false,
7854     store : false,
7855     loadMask : false,
7856     footerShow : true,
7857     headerShow : true,
7858   
7859     rowSelection : false,
7860     cellSelection : false,
7861     layout : false,
7862     
7863     // Roo.Element - the tbody
7864     mainBody: false,
7865     // Roo.Element - thead element
7866     mainHead: false,
7867     
7868     container: false, // used by gridpanel...
7869     
7870     lazyLoad : false,
7871     
7872     CSS : Roo.util.CSS,
7873     
7874     auto_hide_footer : false,
7875     
7876     getAutoCreate : function()
7877     {
7878         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7879         
7880         cfg = {
7881             tag: 'table',
7882             cls : 'table',
7883             cn : []
7884         };
7885         if (this.scrollBody) {
7886             cfg.cls += ' table-body-fixed';
7887         }    
7888         if (this.striped) {
7889             cfg.cls += ' table-striped';
7890         }
7891         
7892         if (this.hover) {
7893             cfg.cls += ' table-hover';
7894         }
7895         if (this.bordered) {
7896             cfg.cls += ' table-bordered';
7897         }
7898         if (this.condensed) {
7899             cfg.cls += ' table-condensed';
7900         }
7901         if (this.responsive) {
7902             cfg.cls += ' table-responsive';
7903         }
7904         
7905         if (this.cls) {
7906             cfg.cls+=  ' ' +this.cls;
7907         }
7908         
7909         // this lot should be simplifed...
7910         var _t = this;
7911         var cp = [
7912             'align',
7913             'bgcolor',
7914             'border',
7915             'cellpadding',
7916             'cellspacing',
7917             'frame',
7918             'rules',
7919             'sortable',
7920             'summary',
7921             'width'
7922         ].forEach(function(k) {
7923             if (_t[k]) {
7924                 cfg[k] = _t[k];
7925             }
7926         });
7927         
7928         
7929         if (this.layout) {
7930             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7931         }
7932         
7933         if(this.store || this.cm){
7934             if(this.headerShow){
7935                 cfg.cn.push(this.renderHeader());
7936             }
7937             
7938             cfg.cn.push(this.renderBody());
7939             
7940             if(this.footerShow){
7941                 cfg.cn.push(this.renderFooter());
7942             }
7943             // where does this come from?
7944             //cfg.cls+=  ' TableGrid';
7945         }
7946         
7947         return { cn : [ cfg ] };
7948     },
7949     
7950     initEvents : function()
7951     {   
7952         if(!this.store || !this.cm){
7953             return;
7954         }
7955         if (this.selModel) {
7956             this.selModel.initEvents();
7957         }
7958         
7959         
7960         //Roo.log('initEvents with ds!!!!');
7961         
7962         this.mainBody = this.el.select('tbody', true).first();
7963         this.mainHead = this.el.select('thead', true).first();
7964         this.mainFoot = this.el.select('tfoot', true).first();
7965         
7966         
7967         
7968         var _this = this;
7969         
7970         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7971             e.on('click', _this.sort, _this);
7972         });
7973         
7974         this.mainBody.on("click", this.onClick, this);
7975         this.mainBody.on("dblclick", this.onDblClick, this);
7976         
7977         // why is this done????? = it breaks dialogs??
7978         //this.parent().el.setStyle('position', 'relative');
7979         
7980         
7981         if (this.footer) {
7982             this.footer.parentId = this.id;
7983             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7984             
7985             if(this.lazyLoad){
7986                 this.el.select('tfoot tr td').first().addClass('hide');
7987             }
7988         } 
7989         
7990         if(this.loadMask) {
7991             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7992         }
7993         
7994         this.store.on('load', this.onLoad, this);
7995         this.store.on('beforeload', this.onBeforeLoad, this);
7996         this.store.on('update', this.onUpdate, this);
7997         this.store.on('add', this.onAdd, this);
7998         this.store.on("clear", this.clear, this);
7999         
8000         this.el.on("contextmenu", this.onContextMenu, this);
8001         
8002         this.mainBody.on('scroll', this.onBodyScroll, this);
8003         
8004         this.cm.on("headerchange", this.onHeaderChange, this);
8005         
8006         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
8007         
8008     },
8009     
8010     onContextMenu : function(e, t)
8011     {
8012         this.processEvent("contextmenu", e);
8013     },
8014     
8015     processEvent : function(name, e)
8016     {
8017         if (name != 'touchstart' ) {
8018             this.fireEvent(name, e);    
8019         }
8020         
8021         var t = e.getTarget();
8022         
8023         var cell = Roo.get(t);
8024         
8025         if(!cell){
8026             return;
8027         }
8028         
8029         if(cell.findParent('tfoot', false, true)){
8030             return;
8031         }
8032         
8033         if(cell.findParent('thead', false, true)){
8034             
8035             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8036                 cell = Roo.get(t).findParent('th', false, true);
8037                 if (!cell) {
8038                     Roo.log("failed to find th in thead?");
8039                     Roo.log(e.getTarget());
8040                     return;
8041                 }
8042             }
8043             
8044             var cellIndex = cell.dom.cellIndex;
8045             
8046             var ename = name == 'touchstart' ? 'click' : name;
8047             this.fireEvent("header" + ename, this, cellIndex, e);
8048             
8049             return;
8050         }
8051         
8052         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8053             cell = Roo.get(t).findParent('td', false, true);
8054             if (!cell) {
8055                 Roo.log("failed to find th in tbody?");
8056                 Roo.log(e.getTarget());
8057                 return;
8058             }
8059         }
8060         
8061         var row = cell.findParent('tr', false, true);
8062         var cellIndex = cell.dom.cellIndex;
8063         var rowIndex = row.dom.rowIndex - 1;
8064         
8065         if(row !== false){
8066             
8067             this.fireEvent("row" + name, this, rowIndex, e);
8068             
8069             if(cell !== false){
8070             
8071                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8072             }
8073         }
8074         
8075     },
8076     
8077     onMouseover : function(e, el)
8078     {
8079         var cell = Roo.get(el);
8080         
8081         if(!cell){
8082             return;
8083         }
8084         
8085         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8086             cell = cell.findParent('td', false, true);
8087         }
8088         
8089         var row = cell.findParent('tr', false, true);
8090         var cellIndex = cell.dom.cellIndex;
8091         var rowIndex = row.dom.rowIndex - 1; // start from 0
8092         
8093         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8094         
8095     },
8096     
8097     onMouseout : function(e, el)
8098     {
8099         var cell = Roo.get(el);
8100         
8101         if(!cell){
8102             return;
8103         }
8104         
8105         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8106             cell = cell.findParent('td', false, true);
8107         }
8108         
8109         var row = cell.findParent('tr', false, true);
8110         var cellIndex = cell.dom.cellIndex;
8111         var rowIndex = row.dom.rowIndex - 1; // start from 0
8112         
8113         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8114         
8115     },
8116     
8117     onClick : function(e, el)
8118     {
8119         var cell = Roo.get(el);
8120         
8121         if(!cell || (!this.cellSelection && !this.rowSelection)){
8122             return;
8123         }
8124         
8125         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8126             cell = cell.findParent('td', false, true);
8127         }
8128         
8129         if(!cell || typeof(cell) == 'undefined'){
8130             return;
8131         }
8132         
8133         var row = cell.findParent('tr', false, true);
8134         
8135         if(!row || typeof(row) == 'undefined'){
8136             return;
8137         }
8138         
8139         var cellIndex = cell.dom.cellIndex;
8140         var rowIndex = this.getRowIndex(row);
8141         
8142         // why??? - should these not be based on SelectionModel?
8143         if(this.cellSelection){
8144             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8145         }
8146         
8147         if(this.rowSelection){
8148             this.fireEvent('rowclick', this, row, rowIndex, e);
8149         }
8150         
8151         
8152     },
8153         
8154     onDblClick : function(e,el)
8155     {
8156         var cell = Roo.get(el);
8157         
8158         if(!cell || (!this.cellSelection && !this.rowSelection)){
8159             return;
8160         }
8161         
8162         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8163             cell = cell.findParent('td', false, true);
8164         }
8165         
8166         if(!cell || typeof(cell) == 'undefined'){
8167             return;
8168         }
8169         
8170         var row = cell.findParent('tr', false, true);
8171         
8172         if(!row || typeof(row) == 'undefined'){
8173             return;
8174         }
8175         
8176         var cellIndex = cell.dom.cellIndex;
8177         var rowIndex = this.getRowIndex(row);
8178         
8179         if(this.cellSelection){
8180             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8181         }
8182         
8183         if(this.rowSelection){
8184             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8185         }
8186     },
8187     
8188     sort : function(e,el)
8189     {
8190         var col = Roo.get(el);
8191         
8192         if(!col.hasClass('sortable')){
8193             return;
8194         }
8195         
8196         var sort = col.attr('sort');
8197         var dir = 'ASC';
8198         
8199         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8200             dir = 'DESC';
8201         }
8202         
8203         this.store.sortInfo = {field : sort, direction : dir};
8204         
8205         if (this.footer) {
8206             Roo.log("calling footer first");
8207             this.footer.onClick('first');
8208         } else {
8209         
8210             this.store.load({ params : { start : 0 } });
8211         }
8212     },
8213     
8214     renderHeader : function()
8215     {
8216         var header = {
8217             tag: 'thead',
8218             cn : []
8219         };
8220         
8221         var cm = this.cm;
8222         this.totalWidth = 0;
8223         
8224         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8225             
8226             var config = cm.config[i];
8227             
8228             var c = {
8229                 tag: 'th',
8230                 cls : 'x-hcol-' + i,
8231                 style : '',
8232                 html: cm.getColumnHeader(i)
8233             };
8234             
8235             var hh = '';
8236             
8237             if(typeof(config.sortable) != 'undefined' && config.sortable){
8238                 c.cls = 'sortable';
8239                 c.html = '<i class="glyphicon"></i>' + c.html;
8240             }
8241             
8242             // could use BS4 hidden-..-down 
8243             
8244             if(typeof(config.lgHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.mdHeader) != 'undefined'){
8249                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8250             }
8251             
8252             if(typeof(config.smHeader) != 'undefined'){
8253                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8254             }
8255             
8256             if(typeof(config.xsHeader) != 'undefined'){
8257                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8258             }
8259             
8260             if(hh.length){
8261                 c.html = hh;
8262             }
8263             
8264             if(typeof(config.tooltip) != 'undefined'){
8265                 c.tooltip = config.tooltip;
8266             }
8267             
8268             if(typeof(config.colspan) != 'undefined'){
8269                 c.colspan = config.colspan;
8270             }
8271             
8272             if(typeof(config.hidden) != 'undefined' && config.hidden){
8273                 c.style += ' display:none;';
8274             }
8275             
8276             if(typeof(config.dataIndex) != 'undefined'){
8277                 c.sort = config.dataIndex;
8278             }
8279             
8280            
8281             
8282             if(typeof(config.align) != 'undefined' && config.align.length){
8283                 c.style += ' text-align:' + config.align + ';';
8284             }
8285             
8286             if(typeof(config.width) != 'undefined'){
8287                 c.style += ' width:' + config.width + 'px;';
8288                 this.totalWidth += config.width;
8289             } else {
8290                 this.totalWidth += 100; // assume minimum of 100 per column?
8291             }
8292             
8293             if(typeof(config.cls) != 'undefined'){
8294                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8295             }
8296             
8297             ['xs','sm','md','lg'].map(function(size){
8298                 
8299                 if(typeof(config[size]) == 'undefined'){
8300                     return;
8301                 }
8302                  
8303                 if (!config[size]) { // 0 = hidden
8304                     // BS 4 '0' is treated as hide that column and below.
8305                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8306                     return;
8307                 }
8308                 
8309                 c.cls += ' col-' + size + '-' + config[size] + (
8310                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8311                 );
8312                 
8313                 
8314             });
8315             
8316             header.cn.push(c)
8317         }
8318         
8319         return header;
8320     },
8321     
8322     renderBody : function()
8323     {
8324         var body = {
8325             tag: 'tbody',
8326             cn : [
8327                 {
8328                     tag: 'tr',
8329                     cn : [
8330                         {
8331                             tag : 'td',
8332                             colspan :  this.cm.getColumnCount()
8333                         }
8334                     ]
8335                 }
8336             ]
8337         };
8338         
8339         return body;
8340     },
8341     
8342     renderFooter : function()
8343     {
8344         var footer = {
8345             tag: 'tfoot',
8346             cn : [
8347                 {
8348                     tag: 'tr',
8349                     cn : [
8350                         {
8351                             tag : 'td',
8352                             colspan :  this.cm.getColumnCount()
8353                         }
8354                     ]
8355                 }
8356             ]
8357         };
8358         
8359         return footer;
8360     },
8361     
8362     
8363     
8364     onLoad : function()
8365     {
8366 //        Roo.log('ds onload');
8367         this.clear();
8368         
8369         var _this = this;
8370         var cm = this.cm;
8371         var ds = this.store;
8372         
8373         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8374             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8375             if (_this.store.sortInfo) {
8376                     
8377                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8378                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8379                 }
8380                 
8381                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8382                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8383                 }
8384             }
8385         });
8386         
8387         var tbody =  this.mainBody;
8388               
8389         if(ds.getCount() > 0){
8390             ds.data.each(function(d,rowIndex){
8391                 var row =  this.renderRow(cm, ds, rowIndex);
8392                 
8393                 tbody.createChild(row);
8394                 
8395                 var _this = this;
8396                 
8397                 if(row.cellObjects.length){
8398                     Roo.each(row.cellObjects, function(r){
8399                         _this.renderCellObject(r);
8400                     })
8401                 }
8402                 
8403             }, this);
8404         }
8405         
8406         var tfoot = this.el.select('tfoot', true).first();
8407         
8408         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8409             
8410             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8411             
8412             var total = this.ds.getTotalCount();
8413             
8414             if(this.footer.pageSize < total){
8415                 this.mainFoot.show();
8416             }
8417         }
8418         
8419         Roo.each(this.el.select('tbody td', true).elements, function(e){
8420             e.on('mouseover', _this.onMouseover, _this);
8421         });
8422         
8423         Roo.each(this.el.select('tbody td', true).elements, function(e){
8424             e.on('mouseout', _this.onMouseout, _this);
8425         });
8426         this.fireEvent('rowsrendered', this);
8427         
8428         this.autoSize();
8429     },
8430     
8431     
8432     onUpdate : function(ds,record)
8433     {
8434         this.refreshRow(record);
8435         this.autoSize();
8436     },
8437     
8438     onRemove : function(ds, record, index, isUpdate){
8439         if(isUpdate !== true){
8440             this.fireEvent("beforerowremoved", this, index, record);
8441         }
8442         var bt = this.mainBody.dom;
8443         
8444         var rows = this.el.select('tbody > tr', true).elements;
8445         
8446         if(typeof(rows[index]) != 'undefined'){
8447             bt.removeChild(rows[index].dom);
8448         }
8449         
8450 //        if(bt.rows[index]){
8451 //            bt.removeChild(bt.rows[index]);
8452 //        }
8453         
8454         if(isUpdate !== true){
8455             //this.stripeRows(index);
8456             //this.syncRowHeights(index, index);
8457             //this.layout();
8458             this.fireEvent("rowremoved", this, index, record);
8459         }
8460     },
8461     
8462     onAdd : function(ds, records, rowIndex)
8463     {
8464         //Roo.log('on Add called');
8465         // - note this does not handle multiple adding very well..
8466         var bt = this.mainBody.dom;
8467         for (var i =0 ; i < records.length;i++) {
8468             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8469             //Roo.log(records[i]);
8470             //Roo.log(this.store.getAt(rowIndex+i));
8471             this.insertRow(this.store, rowIndex + i, false);
8472             return;
8473         }
8474         
8475     },
8476     
8477     
8478     refreshRow : function(record){
8479         var ds = this.store, index;
8480         if(typeof record == 'number'){
8481             index = record;
8482             record = ds.getAt(index);
8483         }else{
8484             index = ds.indexOf(record);
8485             if (index < 0) {
8486                 return; // should not happen - but seems to 
8487             }
8488         }
8489         this.insertRow(ds, index, true);
8490         this.autoSize();
8491         this.onRemove(ds, record, index+1, true);
8492         this.autoSize();
8493         //this.syncRowHeights(index, index);
8494         //this.layout();
8495         this.fireEvent("rowupdated", this, index, record);
8496     },
8497     
8498     insertRow : function(dm, rowIndex, isUpdate){
8499         
8500         if(!isUpdate){
8501             this.fireEvent("beforerowsinserted", this, rowIndex);
8502         }
8503             //var s = this.getScrollState();
8504         var row = this.renderRow(this.cm, this.store, rowIndex);
8505         // insert before rowIndex..
8506         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8507         
8508         var _this = this;
8509                 
8510         if(row.cellObjects.length){
8511             Roo.each(row.cellObjects, function(r){
8512                 _this.renderCellObject(r);
8513             })
8514         }
8515             
8516         if(!isUpdate){
8517             this.fireEvent("rowsinserted", this, rowIndex);
8518             //this.syncRowHeights(firstRow, lastRow);
8519             //this.stripeRows(firstRow);
8520             //this.layout();
8521         }
8522         
8523     },
8524     
8525     
8526     getRowDom : function(rowIndex)
8527     {
8528         var rows = this.el.select('tbody > tr', true).elements;
8529         
8530         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8531         
8532     },
8533     // returns the object tree for a tr..
8534   
8535     
8536     renderRow : function(cm, ds, rowIndex) 
8537     {
8538         var d = ds.getAt(rowIndex);
8539         
8540         var row = {
8541             tag : 'tr',
8542             cls : 'x-row-' + rowIndex,
8543             cn : []
8544         };
8545             
8546         var cellObjects = [];
8547         
8548         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8549             var config = cm.config[i];
8550             
8551             var renderer = cm.getRenderer(i);
8552             var value = '';
8553             var id = false;
8554             
8555             if(typeof(renderer) !== 'undefined'){
8556                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8557             }
8558             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8559             // and are rendered into the cells after the row is rendered - using the id for the element.
8560             
8561             if(typeof(value) === 'object'){
8562                 id = Roo.id();
8563                 cellObjects.push({
8564                     container : id,
8565                     cfg : value 
8566                 })
8567             }
8568             
8569             var rowcfg = {
8570                 record: d,
8571                 rowIndex : rowIndex,
8572                 colIndex : i,
8573                 rowClass : ''
8574             };
8575
8576             this.fireEvent('rowclass', this, rowcfg);
8577             
8578             var td = {
8579                 tag: 'td',
8580                 cls : rowcfg.rowClass + ' x-col-' + i,
8581                 style: '',
8582                 html: (typeof(value) === 'object') ? '' : value
8583             };
8584             
8585             if (id) {
8586                 td.id = id;
8587             }
8588             
8589             if(typeof(config.colspan) != 'undefined'){
8590                 td.colspan = config.colspan;
8591             }
8592             
8593             if(typeof(config.hidden) != 'undefined' && config.hidden){
8594                 td.style += ' display:none;';
8595             }
8596             
8597             if(typeof(config.align) != 'undefined' && config.align.length){
8598                 td.style += ' text-align:' + config.align + ';';
8599             }
8600             if(typeof(config.valign) != 'undefined' && config.valign.length){
8601                 td.style += ' vertical-align:' + config.valign + ';';
8602             }
8603             
8604             if(typeof(config.width) != 'undefined'){
8605                 td.style += ' width:' +  config.width + 'px;';
8606             }
8607             
8608             if(typeof(config.cursor) != 'undefined'){
8609                 td.style += ' cursor:' +  config.cursor + ';';
8610             }
8611             
8612             if(typeof(config.cls) != 'undefined'){
8613                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8614             }
8615             
8616             ['xs','sm','md','lg'].map(function(size){
8617                 
8618                 if(typeof(config[size]) == 'undefined'){
8619                     return;
8620                 }
8621                 
8622                 
8623                   
8624                 if (!config[size]) { // 0 = hidden
8625                     // BS 4 '0' is treated as hide that column and below.
8626                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8627                     return;
8628                 }
8629                 
8630                 td.cls += ' col-' + size + '-' + config[size] + (
8631                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8632                 );
8633                  
8634
8635             });
8636             
8637             row.cn.push(td);
8638            
8639         }
8640         
8641         row.cellObjects = cellObjects;
8642         
8643         return row;
8644           
8645     },
8646     
8647     
8648     
8649     onBeforeLoad : function()
8650     {
8651         
8652     },
8653      /**
8654      * Remove all rows
8655      */
8656     clear : function()
8657     {
8658         this.el.select('tbody', true).first().dom.innerHTML = '';
8659     },
8660     /**
8661      * Show or hide a row.
8662      * @param {Number} rowIndex to show or hide
8663      * @param {Boolean} state hide
8664      */
8665     setRowVisibility : function(rowIndex, state)
8666     {
8667         var bt = this.mainBody.dom;
8668         
8669         var rows = this.el.select('tbody > tr', true).elements;
8670         
8671         if(typeof(rows[rowIndex]) == 'undefined'){
8672             return;
8673         }
8674         rows[rowIndex].dom.style.display = state ? '' : 'none';
8675     },
8676     
8677     
8678     getSelectionModel : function(){
8679         if(!this.selModel){
8680             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8681         }
8682         return this.selModel;
8683     },
8684     /*
8685      * Render the Roo.bootstrap object from renderder
8686      */
8687     renderCellObject : function(r)
8688     {
8689         var _this = this;
8690         
8691         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8692         
8693         var t = r.cfg.render(r.container);
8694         
8695         if(r.cfg.cn){
8696             Roo.each(r.cfg.cn, function(c){
8697                 var child = {
8698                     container: t.getChildContainer(),
8699                     cfg: c
8700                 };
8701                 _this.renderCellObject(child);
8702             })
8703         }
8704     },
8705     
8706     getRowIndex : function(row)
8707     {
8708         var rowIndex = -1;
8709         
8710         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8711             if(el != row){
8712                 return;
8713             }
8714             
8715             rowIndex = index;
8716         });
8717         
8718         return rowIndex;
8719     },
8720      /**
8721      * Returns the grid's underlying element = used by panel.Grid
8722      * @return {Element} The element
8723      */
8724     getGridEl : function(){
8725         return this.el;
8726     },
8727      /**
8728      * Forces a resize - used by panel.Grid
8729      * @return {Element} The element
8730      */
8731     autoSize : function()
8732     {
8733         //var ctr = Roo.get(this.container.dom.parentElement);
8734         var ctr = Roo.get(this.el.dom);
8735         
8736         var thd = this.getGridEl().select('thead',true).first();
8737         var tbd = this.getGridEl().select('tbody', true).first();
8738         var tfd = this.getGridEl().select('tfoot', true).first();
8739         
8740         var cw = ctr.getWidth();
8741         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8742         
8743         if (tbd) {
8744             
8745             tbd.setWidth(ctr.getWidth());
8746             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8747             // this needs fixing for various usage - currently only hydra job advers I think..
8748             //tdb.setHeight(
8749             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8750             //); 
8751             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8752             cw -= barsize;
8753         }
8754         cw = Math.max(cw, this.totalWidth);
8755         this.getGridEl().select('tbody tr',true).setWidth(cw);
8756         
8757         // resize 'expandable coloumn?
8758         
8759         return; // we doe not have a view in this design..
8760         
8761     },
8762     onBodyScroll: function()
8763     {
8764         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8765         if(this.mainHead){
8766             this.mainHead.setStyle({
8767                 'position' : 'relative',
8768                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8769             });
8770         }
8771         
8772         if(this.lazyLoad){
8773             
8774             var scrollHeight = this.mainBody.dom.scrollHeight;
8775             
8776             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8777             
8778             var height = this.mainBody.getHeight();
8779             
8780             if(scrollHeight - height == scrollTop) {
8781                 
8782                 var total = this.ds.getTotalCount();
8783                 
8784                 if(this.footer.cursor + this.footer.pageSize < total){
8785                     
8786                     this.footer.ds.load({
8787                         params : {
8788                             start : this.footer.cursor + this.footer.pageSize,
8789                             limit : this.footer.pageSize
8790                         },
8791                         add : true
8792                     });
8793                 }
8794             }
8795             
8796         }
8797     },
8798     
8799     onHeaderChange : function()
8800     {
8801         var header = this.renderHeader();
8802         var table = this.el.select('table', true).first();
8803         
8804         this.mainHead.remove();
8805         this.mainHead = table.createChild(header, this.mainBody, false);
8806     },
8807     
8808     onHiddenChange : function(colModel, colIndex, hidden)
8809     {
8810         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8811         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8812         
8813         this.CSS.updateRule(thSelector, "display", "");
8814         this.CSS.updateRule(tdSelector, "display", "");
8815         
8816         if(hidden){
8817             this.CSS.updateRule(thSelector, "display", "none");
8818             this.CSS.updateRule(tdSelector, "display", "none");
8819         }
8820         
8821         this.onHeaderChange();
8822         this.onLoad();
8823     },
8824     
8825     setColumnWidth: function(col_index, width)
8826     {
8827         // width = "md-2 xs-2..."
8828         if(!this.colModel.config[col_index]) {
8829             return;
8830         }
8831         
8832         var w = width.split(" ");
8833         
8834         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8835         
8836         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8837         
8838         
8839         for(var j = 0; j < w.length; j++) {
8840             
8841             if(!w[j]) {
8842                 continue;
8843             }
8844             
8845             var size_cls = w[j].split("-");
8846             
8847             if(!Number.isInteger(size_cls[1] * 1)) {
8848                 continue;
8849             }
8850             
8851             if(!this.colModel.config[col_index][size_cls[0]]) {
8852                 continue;
8853             }
8854             
8855             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8856                 continue;
8857             }
8858             
8859             h_row[0].classList.replace(
8860                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8861                 "col-"+size_cls[0]+"-"+size_cls[1]
8862             );
8863             
8864             for(var i = 0; i < rows.length; i++) {
8865                 
8866                 var size_cls = w[j].split("-");
8867                 
8868                 if(!Number.isInteger(size_cls[1] * 1)) {
8869                     continue;
8870                 }
8871                 
8872                 if(!this.colModel.config[col_index][size_cls[0]]) {
8873                     continue;
8874                 }
8875                 
8876                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8877                     continue;
8878                 }
8879                 
8880                 rows[i].classList.replace(
8881                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8882                     "col-"+size_cls[0]+"-"+size_cls[1]
8883                 );
8884             }
8885             
8886             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8887         }
8888     }
8889 });
8890
8891  
8892
8893  /*
8894  * - LGPL
8895  *
8896  * table cell
8897  * 
8898  */
8899
8900 /**
8901  * @class Roo.bootstrap.TableCell
8902  * @extends Roo.bootstrap.Component
8903  * Bootstrap TableCell class
8904  * @cfg {String} html cell contain text
8905  * @cfg {String} cls cell class
8906  * @cfg {String} tag cell tag (td|th) default td
8907  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8908  * @cfg {String} align Aligns the content in a cell
8909  * @cfg {String} axis Categorizes cells
8910  * @cfg {String} bgcolor Specifies the background color of a cell
8911  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8912  * @cfg {Number} colspan Specifies the number of columns a cell should span
8913  * @cfg {String} headers Specifies one or more header cells a cell is related to
8914  * @cfg {Number} height Sets the height of a cell
8915  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8916  * @cfg {Number} rowspan Sets the number of rows a cell should span
8917  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8918  * @cfg {String} valign Vertical aligns the content in a cell
8919  * @cfg {Number} width Specifies the width of a cell
8920  * 
8921  * @constructor
8922  * Create a new TableCell
8923  * @param {Object} config The config object
8924  */
8925
8926 Roo.bootstrap.TableCell = function(config){
8927     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8928 };
8929
8930 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8931     
8932     html: false,
8933     cls: false,
8934     tag: false,
8935     abbr: false,
8936     align: false,
8937     axis: false,
8938     bgcolor: false,
8939     charoff: false,
8940     colspan: false,
8941     headers: false,
8942     height: false,
8943     nowrap: false,
8944     rowspan: false,
8945     scope: false,
8946     valign: false,
8947     width: false,
8948     
8949     
8950     getAutoCreate : function(){
8951         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8952         
8953         cfg = {
8954             tag: 'td'
8955         };
8956         
8957         if(this.tag){
8958             cfg.tag = this.tag;
8959         }
8960         
8961         if (this.html) {
8962             cfg.html=this.html
8963         }
8964         if (this.cls) {
8965             cfg.cls=this.cls
8966         }
8967         if (this.abbr) {
8968             cfg.abbr=this.abbr
8969         }
8970         if (this.align) {
8971             cfg.align=this.align
8972         }
8973         if (this.axis) {
8974             cfg.axis=this.axis
8975         }
8976         if (this.bgcolor) {
8977             cfg.bgcolor=this.bgcolor
8978         }
8979         if (this.charoff) {
8980             cfg.charoff=this.charoff
8981         }
8982         if (this.colspan) {
8983             cfg.colspan=this.colspan
8984         }
8985         if (this.headers) {
8986             cfg.headers=this.headers
8987         }
8988         if (this.height) {
8989             cfg.height=this.height
8990         }
8991         if (this.nowrap) {
8992             cfg.nowrap=this.nowrap
8993         }
8994         if (this.rowspan) {
8995             cfg.rowspan=this.rowspan
8996         }
8997         if (this.scope) {
8998             cfg.scope=this.scope
8999         }
9000         if (this.valign) {
9001             cfg.valign=this.valign
9002         }
9003         if (this.width) {
9004             cfg.width=this.width
9005         }
9006         
9007         
9008         return cfg;
9009     }
9010    
9011 });
9012
9013  
9014
9015  /*
9016  * - LGPL
9017  *
9018  * table row
9019  * 
9020  */
9021
9022 /**
9023  * @class Roo.bootstrap.TableRow
9024  * @extends Roo.bootstrap.Component
9025  * Bootstrap TableRow class
9026  * @cfg {String} cls row class
9027  * @cfg {String} align Aligns the content in a table row
9028  * @cfg {String} bgcolor Specifies a background color for a table row
9029  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9030  * @cfg {String} valign Vertical aligns the content in a table row
9031  * 
9032  * @constructor
9033  * Create a new TableRow
9034  * @param {Object} config The config object
9035  */
9036
9037 Roo.bootstrap.TableRow = function(config){
9038     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9039 };
9040
9041 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9042     
9043     cls: false,
9044     align: false,
9045     bgcolor: false,
9046     charoff: false,
9047     valign: false,
9048     
9049     getAutoCreate : function(){
9050         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9051         
9052         cfg = {
9053             tag: 'tr'
9054         };
9055             
9056         if(this.cls){
9057             cfg.cls = this.cls;
9058         }
9059         if(this.align){
9060             cfg.align = this.align;
9061         }
9062         if(this.bgcolor){
9063             cfg.bgcolor = this.bgcolor;
9064         }
9065         if(this.charoff){
9066             cfg.charoff = this.charoff;
9067         }
9068         if(this.valign){
9069             cfg.valign = this.valign;
9070         }
9071         
9072         return cfg;
9073     }
9074    
9075 });
9076
9077  
9078
9079  /*
9080  * - LGPL
9081  *
9082  * table body
9083  * 
9084  */
9085
9086 /**
9087  * @class Roo.bootstrap.TableBody
9088  * @extends Roo.bootstrap.Component
9089  * Bootstrap TableBody class
9090  * @cfg {String} cls element class
9091  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9092  * @cfg {String} align Aligns the content inside the element
9093  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9094  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9095  * 
9096  * @constructor
9097  * Create a new TableBody
9098  * @param {Object} config The config object
9099  */
9100
9101 Roo.bootstrap.TableBody = function(config){
9102     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9103 };
9104
9105 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9106     
9107     cls: false,
9108     tag: false,
9109     align: false,
9110     charoff: false,
9111     valign: false,
9112     
9113     getAutoCreate : function(){
9114         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9115         
9116         cfg = {
9117             tag: 'tbody'
9118         };
9119             
9120         if (this.cls) {
9121             cfg.cls=this.cls
9122         }
9123         if(this.tag){
9124             cfg.tag = this.tag;
9125         }
9126         
9127         if(this.align){
9128             cfg.align = this.align;
9129         }
9130         if(this.charoff){
9131             cfg.charoff = this.charoff;
9132         }
9133         if(this.valign){
9134             cfg.valign = this.valign;
9135         }
9136         
9137         return cfg;
9138     }
9139     
9140     
9141 //    initEvents : function()
9142 //    {
9143 //        
9144 //        if(!this.store){
9145 //            return;
9146 //        }
9147 //        
9148 //        this.store = Roo.factory(this.store, Roo.data);
9149 //        this.store.on('load', this.onLoad, this);
9150 //        
9151 //        this.store.load();
9152 //        
9153 //    },
9154 //    
9155 //    onLoad: function () 
9156 //    {   
9157 //        this.fireEvent('load', this);
9158 //    }
9159 //    
9160 //   
9161 });
9162
9163  
9164
9165  /*
9166  * Based on:
9167  * Ext JS Library 1.1.1
9168  * Copyright(c) 2006-2007, Ext JS, LLC.
9169  *
9170  * Originally Released Under LGPL - original licence link has changed is not relivant.
9171  *
9172  * Fork - LGPL
9173  * <script type="text/javascript">
9174  */
9175
9176 // as we use this in bootstrap.
9177 Roo.namespace('Roo.form');
9178  /**
9179  * @class Roo.form.Action
9180  * Internal Class used to handle form actions
9181  * @constructor
9182  * @param {Roo.form.BasicForm} el The form element or its id
9183  * @param {Object} config Configuration options
9184  */
9185
9186  
9187  
9188 // define the action interface
9189 Roo.form.Action = function(form, options){
9190     this.form = form;
9191     this.options = options || {};
9192 };
9193 /**
9194  * Client Validation Failed
9195  * @const 
9196  */
9197 Roo.form.Action.CLIENT_INVALID = 'client';
9198 /**
9199  * Server Validation Failed
9200  * @const 
9201  */
9202 Roo.form.Action.SERVER_INVALID = 'server';
9203  /**
9204  * Connect to Server Failed
9205  * @const 
9206  */
9207 Roo.form.Action.CONNECT_FAILURE = 'connect';
9208 /**
9209  * Reading Data from Server Failed
9210  * @const 
9211  */
9212 Roo.form.Action.LOAD_FAILURE = 'load';
9213
9214 Roo.form.Action.prototype = {
9215     type : 'default',
9216     failureType : undefined,
9217     response : undefined,
9218     result : undefined,
9219
9220     // interface method
9221     run : function(options){
9222
9223     },
9224
9225     // interface method
9226     success : function(response){
9227
9228     },
9229
9230     // interface method
9231     handleResponse : function(response){
9232
9233     },
9234
9235     // default connection failure
9236     failure : function(response){
9237         
9238         this.response = response;
9239         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9240         this.form.afterAction(this, false);
9241     },
9242
9243     processResponse : function(response){
9244         this.response = response;
9245         if(!response.responseText){
9246             return true;
9247         }
9248         this.result = this.handleResponse(response);
9249         return this.result;
9250     },
9251
9252     // utility functions used internally
9253     getUrl : function(appendParams){
9254         var url = this.options.url || this.form.url || this.form.el.dom.action;
9255         if(appendParams){
9256             var p = this.getParams();
9257             if(p){
9258                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9259             }
9260         }
9261         return url;
9262     },
9263
9264     getMethod : function(){
9265         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9266     },
9267
9268     getParams : function(){
9269         var bp = this.form.baseParams;
9270         var p = this.options.params;
9271         if(p){
9272             if(typeof p == "object"){
9273                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9274             }else if(typeof p == 'string' && bp){
9275                 p += '&' + Roo.urlEncode(bp);
9276             }
9277         }else if(bp){
9278             p = Roo.urlEncode(bp);
9279         }
9280         return p;
9281     },
9282
9283     createCallback : function(){
9284         return {
9285             success: this.success,
9286             failure: this.failure,
9287             scope: this,
9288             timeout: (this.form.timeout*1000),
9289             upload: this.form.fileUpload ? this.success : undefined
9290         };
9291     }
9292 };
9293
9294 Roo.form.Action.Submit = function(form, options){
9295     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9296 };
9297
9298 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9299     type : 'submit',
9300
9301     haveProgress : false,
9302     uploadComplete : false,
9303     
9304     // uploadProgress indicator.
9305     uploadProgress : function()
9306     {
9307         if (!this.form.progressUrl) {
9308             return;
9309         }
9310         
9311         if (!this.haveProgress) {
9312             Roo.MessageBox.progress("Uploading", "Uploading");
9313         }
9314         if (this.uploadComplete) {
9315            Roo.MessageBox.hide();
9316            return;
9317         }
9318         
9319         this.haveProgress = true;
9320    
9321         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9322         
9323         var c = new Roo.data.Connection();
9324         c.request({
9325             url : this.form.progressUrl,
9326             params: {
9327                 id : uid
9328             },
9329             method: 'GET',
9330             success : function(req){
9331                //console.log(data);
9332                 var rdata = false;
9333                 var edata;
9334                 try  {
9335                    rdata = Roo.decode(req.responseText)
9336                 } catch (e) {
9337                     Roo.log("Invalid data from server..");
9338                     Roo.log(edata);
9339                     return;
9340                 }
9341                 if (!rdata || !rdata.success) {
9342                     Roo.log(rdata);
9343                     Roo.MessageBox.alert(Roo.encode(rdata));
9344                     return;
9345                 }
9346                 var data = rdata.data;
9347                 
9348                 if (this.uploadComplete) {
9349                    Roo.MessageBox.hide();
9350                    return;
9351                 }
9352                    
9353                 if (data){
9354                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9355                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9356                     );
9357                 }
9358                 this.uploadProgress.defer(2000,this);
9359             },
9360        
9361             failure: function(data) {
9362                 Roo.log('progress url failed ');
9363                 Roo.log(data);
9364             },
9365             scope : this
9366         });
9367            
9368     },
9369     
9370     
9371     run : function()
9372     {
9373         // run get Values on the form, so it syncs any secondary forms.
9374         this.form.getValues();
9375         
9376         var o = this.options;
9377         var method = this.getMethod();
9378         var isPost = method == 'POST';
9379         if(o.clientValidation === false || this.form.isValid()){
9380             
9381             if (this.form.progressUrl) {
9382                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9383                     (new Date() * 1) + '' + Math.random());
9384                     
9385             } 
9386             
9387             
9388             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9389                 form:this.form.el.dom,
9390                 url:this.getUrl(!isPost),
9391                 method: method,
9392                 params:isPost ? this.getParams() : null,
9393                 isUpload: this.form.fileUpload,
9394                 formData : this.form.formData
9395             }));
9396             
9397             this.uploadProgress();
9398
9399         }else if (o.clientValidation !== false){ // client validation failed
9400             this.failureType = Roo.form.Action.CLIENT_INVALID;
9401             this.form.afterAction(this, false);
9402         }
9403     },
9404
9405     success : function(response)
9406     {
9407         this.uploadComplete= true;
9408         if (this.haveProgress) {
9409             Roo.MessageBox.hide();
9410         }
9411         
9412         
9413         var result = this.processResponse(response);
9414         if(result === true || result.success){
9415             this.form.afterAction(this, true);
9416             return;
9417         }
9418         if(result.errors){
9419             this.form.markInvalid(result.errors);
9420             this.failureType = Roo.form.Action.SERVER_INVALID;
9421         }
9422         this.form.afterAction(this, false);
9423     },
9424     failure : function(response)
9425     {
9426         this.uploadComplete= true;
9427         if (this.haveProgress) {
9428             Roo.MessageBox.hide();
9429         }
9430         
9431         this.response = response;
9432         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9433         this.form.afterAction(this, false);
9434     },
9435     
9436     handleResponse : function(response){
9437         if(this.form.errorReader){
9438             var rs = this.form.errorReader.read(response);
9439             var errors = [];
9440             if(rs.records){
9441                 for(var i = 0, len = rs.records.length; i < len; i++) {
9442                     var r = rs.records[i];
9443                     errors[i] = r.data;
9444                 }
9445             }
9446             if(errors.length < 1){
9447                 errors = null;
9448             }
9449             return {
9450                 success : rs.success,
9451                 errors : errors
9452             };
9453         }
9454         var ret = false;
9455         try {
9456             ret = Roo.decode(response.responseText);
9457         } catch (e) {
9458             ret = {
9459                 success: false,
9460                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9461                 errors : []
9462             };
9463         }
9464         return ret;
9465         
9466     }
9467 });
9468
9469
9470 Roo.form.Action.Load = function(form, options){
9471     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9472     this.reader = this.form.reader;
9473 };
9474
9475 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9476     type : 'load',
9477
9478     run : function(){
9479         
9480         Roo.Ajax.request(Roo.apply(
9481                 this.createCallback(), {
9482                     method:this.getMethod(),
9483                     url:this.getUrl(false),
9484                     params:this.getParams()
9485         }));
9486     },
9487
9488     success : function(response){
9489         
9490         var result = this.processResponse(response);
9491         if(result === true || !result.success || !result.data){
9492             this.failureType = Roo.form.Action.LOAD_FAILURE;
9493             this.form.afterAction(this, false);
9494             return;
9495         }
9496         this.form.clearInvalid();
9497         this.form.setValues(result.data);
9498         this.form.afterAction(this, true);
9499     },
9500
9501     handleResponse : function(response){
9502         if(this.form.reader){
9503             var rs = this.form.reader.read(response);
9504             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9505             return {
9506                 success : rs.success,
9507                 data : data
9508             };
9509         }
9510         return Roo.decode(response.responseText);
9511     }
9512 });
9513
9514 Roo.form.Action.ACTION_TYPES = {
9515     'load' : Roo.form.Action.Load,
9516     'submit' : Roo.form.Action.Submit
9517 };/*
9518  * - LGPL
9519  *
9520  * form
9521  *
9522  */
9523
9524 /**
9525  * @class Roo.bootstrap.Form
9526  * @extends Roo.bootstrap.Component
9527  * Bootstrap Form class
9528  * @cfg {String} method  GET | POST (default POST)
9529  * @cfg {String} labelAlign top | left (default top)
9530  * @cfg {String} align left  | right - for navbars
9531  * @cfg {Boolean} loadMask load mask when submit (default true)
9532
9533  *
9534  * @constructor
9535  * Create a new Form
9536  * @param {Object} config The config object
9537  */
9538
9539
9540 Roo.bootstrap.Form = function(config){
9541     
9542     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9543     
9544     Roo.bootstrap.Form.popover.apply();
9545     
9546     this.addEvents({
9547         /**
9548          * @event clientvalidation
9549          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9550          * @param {Form} this
9551          * @param {Boolean} valid true if the form has passed client-side validation
9552          */
9553         clientvalidation: true,
9554         /**
9555          * @event beforeaction
9556          * Fires before any action is performed. Return false to cancel the action.
9557          * @param {Form} this
9558          * @param {Action} action The action to be performed
9559          */
9560         beforeaction: true,
9561         /**
9562          * @event actionfailed
9563          * Fires when an action fails.
9564          * @param {Form} this
9565          * @param {Action} action The action that failed
9566          */
9567         actionfailed : true,
9568         /**
9569          * @event actioncomplete
9570          * Fires when an action is completed.
9571          * @param {Form} this
9572          * @param {Action} action The action that completed
9573          */
9574         actioncomplete : true
9575     });
9576 };
9577
9578 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9579
9580      /**
9581      * @cfg {String} method
9582      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9583      */
9584     method : 'POST',
9585     /**
9586      * @cfg {String} url
9587      * The URL to use for form actions if one isn't supplied in the action options.
9588      */
9589     /**
9590      * @cfg {Boolean} fileUpload
9591      * Set to true if this form is a file upload.
9592      */
9593
9594     /**
9595      * @cfg {Object} baseParams
9596      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9597      */
9598
9599     /**
9600      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9601      */
9602     timeout: 30,
9603     /**
9604      * @cfg {Sting} align (left|right) for navbar forms
9605      */
9606     align : 'left',
9607
9608     // private
9609     activeAction : null,
9610
9611     /**
9612      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9613      * element by passing it or its id or mask the form itself by passing in true.
9614      * @type Mixed
9615      */
9616     waitMsgTarget : false,
9617
9618     loadMask : true,
9619     
9620     /**
9621      * @cfg {Boolean} errorMask (true|false) default false
9622      */
9623     errorMask : false,
9624     
9625     /**
9626      * @cfg {Number} maskOffset Default 100
9627      */
9628     maskOffset : 100,
9629     
9630     /**
9631      * @cfg {Boolean} maskBody
9632      */
9633     maskBody : false,
9634
9635     getAutoCreate : function(){
9636
9637         var cfg = {
9638             tag: 'form',
9639             method : this.method || 'POST',
9640             id : this.id || Roo.id(),
9641             cls : ''
9642         };
9643         if (this.parent().xtype.match(/^Nav/)) {
9644             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9645
9646         }
9647
9648         if (this.labelAlign == 'left' ) {
9649             cfg.cls += ' form-horizontal';
9650         }
9651
9652
9653         return cfg;
9654     },
9655     initEvents : function()
9656     {
9657         this.el.on('submit', this.onSubmit, this);
9658         // this was added as random key presses on the form where triggering form submit.
9659         this.el.on('keypress', function(e) {
9660             if (e.getCharCode() != 13) {
9661                 return true;
9662             }
9663             // we might need to allow it for textareas.. and some other items.
9664             // check e.getTarget().
9665
9666             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9667                 return true;
9668             }
9669
9670             Roo.log("keypress blocked");
9671
9672             e.preventDefault();
9673             return false;
9674         });
9675         
9676     },
9677     // private
9678     onSubmit : function(e){
9679         e.stopEvent();
9680     },
9681
9682      /**
9683      * Returns true if client-side validation on the form is successful.
9684      * @return Boolean
9685      */
9686     isValid : function(){
9687         var items = this.getItems();
9688         var valid = true;
9689         var target = false;
9690         
9691         items.each(function(f){
9692             
9693             if(f.validate()){
9694                 return;
9695             }
9696             
9697             Roo.log('invalid field: ' + f.name);
9698             
9699             valid = false;
9700
9701             if(!target && f.el.isVisible(true)){
9702                 target = f;
9703             }
9704            
9705         });
9706         
9707         if(this.errorMask && !valid){
9708             Roo.bootstrap.Form.popover.mask(this, target);
9709         }
9710         
9711         return valid;
9712     },
9713     
9714     /**
9715      * Returns true if any fields in this form have changed since their original load.
9716      * @return Boolean
9717      */
9718     isDirty : function(){
9719         var dirty = false;
9720         var items = this.getItems();
9721         items.each(function(f){
9722            if(f.isDirty()){
9723                dirty = true;
9724                return false;
9725            }
9726            return true;
9727         });
9728         return dirty;
9729     },
9730      /**
9731      * Performs a predefined action (submit or load) or custom actions you define on this form.
9732      * @param {String} actionName The name of the action type
9733      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9734      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9735      * accept other config options):
9736      * <pre>
9737 Property          Type             Description
9738 ----------------  ---------------  ----------------------------------------------------------------------------------
9739 url               String           The url for the action (defaults to the form's url)
9740 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9741 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9742 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9743                                    validate the form on the client (defaults to false)
9744      * </pre>
9745      * @return {BasicForm} this
9746      */
9747     doAction : function(action, options){
9748         if(typeof action == 'string'){
9749             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9750         }
9751         if(this.fireEvent('beforeaction', this, action) !== false){
9752             this.beforeAction(action);
9753             action.run.defer(100, action);
9754         }
9755         return this;
9756     },
9757
9758     // private
9759     beforeAction : function(action){
9760         var o = action.options;
9761         
9762         if(this.loadMask){
9763             
9764             if(this.maskBody){
9765                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9766             } else {
9767                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9768             }
9769         }
9770         // not really supported yet.. ??
9771
9772         //if(this.waitMsgTarget === true){
9773         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9774         //}else if(this.waitMsgTarget){
9775         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9776         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9777         //}else {
9778         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9779        // }
9780
9781     },
9782
9783     // private
9784     afterAction : function(action, success){
9785         this.activeAction = null;
9786         var o = action.options;
9787
9788         if(this.loadMask){
9789             
9790             if(this.maskBody){
9791                 Roo.get(document.body).unmask();
9792             } else {
9793                 this.el.unmask();
9794             }
9795         }
9796         
9797         //if(this.waitMsgTarget === true){
9798 //            this.el.unmask();
9799         //}else if(this.waitMsgTarget){
9800         //    this.waitMsgTarget.unmask();
9801         //}else{
9802         //    Roo.MessageBox.updateProgress(1);
9803         //    Roo.MessageBox.hide();
9804        // }
9805         //
9806         if(success){
9807             if(o.reset){
9808                 this.reset();
9809             }
9810             Roo.callback(o.success, o.scope, [this, action]);
9811             this.fireEvent('actioncomplete', this, action);
9812
9813         }else{
9814
9815             // failure condition..
9816             // we have a scenario where updates need confirming.
9817             // eg. if a locking scenario exists..
9818             // we look for { errors : { needs_confirm : true }} in the response.
9819             if (
9820                 (typeof(action.result) != 'undefined')  &&
9821                 (typeof(action.result.errors) != 'undefined')  &&
9822                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9823            ){
9824                 var _t = this;
9825                 Roo.log("not supported yet");
9826                  /*
9827
9828                 Roo.MessageBox.confirm(
9829                     "Change requires confirmation",
9830                     action.result.errorMsg,
9831                     function(r) {
9832                         if (r != 'yes') {
9833                             return;
9834                         }
9835                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9836                     }
9837
9838                 );
9839                 */
9840
9841
9842                 return;
9843             }
9844
9845             Roo.callback(o.failure, o.scope, [this, action]);
9846             // show an error message if no failed handler is set..
9847             if (!this.hasListener('actionfailed')) {
9848                 Roo.log("need to add dialog support");
9849                 /*
9850                 Roo.MessageBox.alert("Error",
9851                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9852                         action.result.errorMsg :
9853                         "Saving Failed, please check your entries or try again"
9854                 );
9855                 */
9856             }
9857
9858             this.fireEvent('actionfailed', this, action);
9859         }
9860
9861     },
9862     /**
9863      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9864      * @param {String} id The value to search for
9865      * @return Field
9866      */
9867     findField : function(id){
9868         var items = this.getItems();
9869         var field = items.get(id);
9870         if(!field){
9871              items.each(function(f){
9872                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9873                     field = f;
9874                     return false;
9875                 }
9876                 return true;
9877             });
9878         }
9879         return field || null;
9880     },
9881      /**
9882      * Mark fields in this form invalid in bulk.
9883      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9884      * @return {BasicForm} this
9885      */
9886     markInvalid : function(errors){
9887         if(errors instanceof Array){
9888             for(var i = 0, len = errors.length; i < len; i++){
9889                 var fieldError = errors[i];
9890                 var f = this.findField(fieldError.id);
9891                 if(f){
9892                     f.markInvalid(fieldError.msg);
9893                 }
9894             }
9895         }else{
9896             var field, id;
9897             for(id in errors){
9898                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9899                     field.markInvalid(errors[id]);
9900                 }
9901             }
9902         }
9903         //Roo.each(this.childForms || [], function (f) {
9904         //    f.markInvalid(errors);
9905         //});
9906
9907         return this;
9908     },
9909
9910     /**
9911      * Set values for fields in this form in bulk.
9912      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9913      * @return {BasicForm} this
9914      */
9915     setValues : function(values){
9916         if(values instanceof Array){ // array of objects
9917             for(var i = 0, len = values.length; i < len; i++){
9918                 var v = values[i];
9919                 var f = this.findField(v.id);
9920                 if(f){
9921                     f.setValue(v.value);
9922                     if(this.trackResetOnLoad){
9923                         f.originalValue = f.getValue();
9924                     }
9925                 }
9926             }
9927         }else{ // object hash
9928             var field, id;
9929             for(id in values){
9930                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9931
9932                     if (field.setFromData &&
9933                         field.valueField &&
9934                         field.displayField &&
9935                         // combos' with local stores can
9936                         // be queried via setValue()
9937                         // to set their value..
9938                         (field.store && !field.store.isLocal)
9939                         ) {
9940                         // it's a combo
9941                         var sd = { };
9942                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9943                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9944                         field.setFromData(sd);
9945
9946                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9947                         
9948                         field.setFromData(values);
9949                         
9950                     } else {
9951                         field.setValue(values[id]);
9952                     }
9953
9954
9955                     if(this.trackResetOnLoad){
9956                         field.originalValue = field.getValue();
9957                     }
9958                 }
9959             }
9960         }
9961
9962         //Roo.each(this.childForms || [], function (f) {
9963         //    f.setValues(values);
9964         //});
9965
9966         return this;
9967     },
9968
9969     /**
9970      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9971      * they are returned as an array.
9972      * @param {Boolean} asString
9973      * @return {Object}
9974      */
9975     getValues : function(asString){
9976         //if (this.childForms) {
9977             // copy values from the child forms
9978         //    Roo.each(this.childForms, function (f) {
9979         //        this.setValues(f.getValues());
9980         //    }, this);
9981         //}
9982
9983
9984
9985         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9986         if(asString === true){
9987             return fs;
9988         }
9989         return Roo.urlDecode(fs);
9990     },
9991
9992     /**
9993      * Returns the fields in this form as an object with key/value pairs.
9994      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9995      * @return {Object}
9996      */
9997     getFieldValues : function(with_hidden)
9998     {
9999         var items = this.getItems();
10000         var ret = {};
10001         items.each(function(f){
10002             
10003             if (!f.getName()) {
10004                 return;
10005             }
10006             
10007             var v = f.getValue();
10008             
10009             if (f.inputType =='radio') {
10010                 if (typeof(ret[f.getName()]) == 'undefined') {
10011                     ret[f.getName()] = ''; // empty..
10012                 }
10013
10014                 if (!f.el.dom.checked) {
10015                     return;
10016
10017                 }
10018                 v = f.el.dom.value;
10019
10020             }
10021             
10022             if(f.xtype == 'MoneyField'){
10023                 ret[f.currencyName] = f.getCurrency();
10024             }
10025
10026             // not sure if this supported any more..
10027             if ((typeof(v) == 'object') && f.getRawValue) {
10028                 v = f.getRawValue() ; // dates..
10029             }
10030             // combo boxes where name != hiddenName...
10031             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10032                 ret[f.name] = f.getRawValue();
10033             }
10034             ret[f.getName()] = v;
10035         });
10036
10037         return ret;
10038     },
10039
10040     /**
10041      * Clears all invalid messages in this form.
10042      * @return {BasicForm} this
10043      */
10044     clearInvalid : function(){
10045         var items = this.getItems();
10046
10047         items.each(function(f){
10048            f.clearInvalid();
10049         });
10050
10051         return this;
10052     },
10053
10054     /**
10055      * Resets this form.
10056      * @return {BasicForm} this
10057      */
10058     reset : function(){
10059         var items = this.getItems();
10060         items.each(function(f){
10061             f.reset();
10062         });
10063
10064         Roo.each(this.childForms || [], function (f) {
10065             f.reset();
10066         });
10067
10068
10069         return this;
10070     },
10071     
10072     getItems : function()
10073     {
10074         var r=new Roo.util.MixedCollection(false, function(o){
10075             return o.id || (o.id = Roo.id());
10076         });
10077         var iter = function(el) {
10078             if (el.inputEl) {
10079                 r.add(el);
10080             }
10081             if (!el.items) {
10082                 return;
10083             }
10084             Roo.each(el.items,function(e) {
10085                 iter(e);
10086             });
10087         };
10088
10089         iter(this);
10090         return r;
10091     },
10092     
10093     hideFields : function(items)
10094     {
10095         Roo.each(items, function(i){
10096             
10097             var f = this.findField(i);
10098             
10099             if(!f){
10100                 return;
10101             }
10102             
10103             f.hide();
10104             
10105         }, this);
10106     },
10107     
10108     showFields : function(items)
10109     {
10110         Roo.each(items, function(i){
10111             
10112             var f = this.findField(i);
10113             
10114             if(!f){
10115                 return;
10116             }
10117             
10118             f.show();
10119             
10120         }, this);
10121     }
10122
10123 });
10124
10125 Roo.apply(Roo.bootstrap.Form, {
10126     
10127     popover : {
10128         
10129         padding : 5,
10130         
10131         isApplied : false,
10132         
10133         isMasked : false,
10134         
10135         form : false,
10136         
10137         target : false,
10138         
10139         toolTip : false,
10140         
10141         intervalID : false,
10142         
10143         maskEl : false,
10144         
10145         apply : function()
10146         {
10147             if(this.isApplied){
10148                 return;
10149             }
10150             
10151             this.maskEl = {
10152                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10153                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10154                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10155                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10156             };
10157             
10158             this.maskEl.top.enableDisplayMode("block");
10159             this.maskEl.left.enableDisplayMode("block");
10160             this.maskEl.bottom.enableDisplayMode("block");
10161             this.maskEl.right.enableDisplayMode("block");
10162             
10163             this.toolTip = new Roo.bootstrap.Tooltip({
10164                 cls : 'roo-form-error-popover',
10165                 alignment : {
10166                     'left' : ['r-l', [-2,0], 'right'],
10167                     'right' : ['l-r', [2,0], 'left'],
10168                     'bottom' : ['tl-bl', [0,2], 'top'],
10169                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10170                 }
10171             });
10172             
10173             this.toolTip.render(Roo.get(document.body));
10174
10175             this.toolTip.el.enableDisplayMode("block");
10176             
10177             Roo.get(document.body).on('click', function(){
10178                 this.unmask();
10179             }, this);
10180             
10181             Roo.get(document.body).on('touchstart', function(){
10182                 this.unmask();
10183             }, this);
10184             
10185             this.isApplied = true
10186         },
10187         
10188         mask : function(form, target)
10189         {
10190             this.form = form;
10191             
10192             this.target = target;
10193             
10194             if(!this.form.errorMask || !target.el){
10195                 return;
10196             }
10197             
10198             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10199             
10200             Roo.log(scrollable);
10201             
10202             var ot = this.target.el.calcOffsetsTo(scrollable);
10203             
10204             var scrollTo = ot[1] - this.form.maskOffset;
10205             
10206             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10207             
10208             scrollable.scrollTo('top', scrollTo);
10209             
10210             var box = this.target.el.getBox();
10211             Roo.log(box);
10212             var zIndex = Roo.bootstrap.Modal.zIndex++;
10213
10214             
10215             this.maskEl.top.setStyle('position', 'absolute');
10216             this.maskEl.top.setStyle('z-index', zIndex);
10217             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10218             this.maskEl.top.setLeft(0);
10219             this.maskEl.top.setTop(0);
10220             this.maskEl.top.show();
10221             
10222             this.maskEl.left.setStyle('position', 'absolute');
10223             this.maskEl.left.setStyle('z-index', zIndex);
10224             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10225             this.maskEl.left.setLeft(0);
10226             this.maskEl.left.setTop(box.y - this.padding);
10227             this.maskEl.left.show();
10228
10229             this.maskEl.bottom.setStyle('position', 'absolute');
10230             this.maskEl.bottom.setStyle('z-index', zIndex);
10231             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10232             this.maskEl.bottom.setLeft(0);
10233             this.maskEl.bottom.setTop(box.bottom + this.padding);
10234             this.maskEl.bottom.show();
10235
10236             this.maskEl.right.setStyle('position', 'absolute');
10237             this.maskEl.right.setStyle('z-index', zIndex);
10238             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10239             this.maskEl.right.setLeft(box.right + this.padding);
10240             this.maskEl.right.setTop(box.y - this.padding);
10241             this.maskEl.right.show();
10242
10243             this.toolTip.bindEl = this.target.el;
10244
10245             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10246
10247             var tip = this.target.blankText;
10248
10249             if(this.target.getValue() !== '' ) {
10250                 
10251                 if (this.target.invalidText.length) {
10252                     tip = this.target.invalidText;
10253                 } else if (this.target.regexText.length){
10254                     tip = this.target.regexText;
10255                 }
10256             }
10257
10258             this.toolTip.show(tip);
10259
10260             this.intervalID = window.setInterval(function() {
10261                 Roo.bootstrap.Form.popover.unmask();
10262             }, 10000);
10263
10264             window.onwheel = function(){ return false;};
10265             
10266             (function(){ this.isMasked = true; }).defer(500, this);
10267             
10268         },
10269         
10270         unmask : function()
10271         {
10272             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10273                 return;
10274             }
10275             
10276             this.maskEl.top.setStyle('position', 'absolute');
10277             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.top.hide();
10279
10280             this.maskEl.left.setStyle('position', 'absolute');
10281             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.left.hide();
10283
10284             this.maskEl.bottom.setStyle('position', 'absolute');
10285             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10286             this.maskEl.bottom.hide();
10287
10288             this.maskEl.right.setStyle('position', 'absolute');
10289             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10290             this.maskEl.right.hide();
10291             
10292             this.toolTip.hide();
10293             
10294             this.toolTip.el.hide();
10295             
10296             window.onwheel = function(){ return true;};
10297             
10298             if(this.intervalID){
10299                 window.clearInterval(this.intervalID);
10300                 this.intervalID = false;
10301             }
10302             
10303             this.isMasked = false;
10304             
10305         }
10306         
10307     }
10308     
10309 });
10310
10311 /*
10312  * Based on:
10313  * Ext JS Library 1.1.1
10314  * Copyright(c) 2006-2007, Ext JS, LLC.
10315  *
10316  * Originally Released Under LGPL - original licence link has changed is not relivant.
10317  *
10318  * Fork - LGPL
10319  * <script type="text/javascript">
10320  */
10321 /**
10322  * @class Roo.form.VTypes
10323  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10324  * @singleton
10325  */
10326 Roo.form.VTypes = function(){
10327     // closure these in so they are only created once.
10328     var alpha = /^[a-zA-Z_]+$/;
10329     var alphanum = /^[a-zA-Z0-9_]+$/;
10330     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10331     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10332
10333     // All these messages and functions are configurable
10334     return {
10335         /**
10336          * The function used to validate email addresses
10337          * @param {String} value The email address
10338          */
10339         'email' : function(v){
10340             return email.test(v);
10341         },
10342         /**
10343          * The error text to display when the email validation function returns false
10344          * @type String
10345          */
10346         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10347         /**
10348          * The keystroke filter mask to be applied on email input
10349          * @type RegExp
10350          */
10351         'emailMask' : /[a-z0-9_\.\-@]/i,
10352
10353         /**
10354          * The function used to validate URLs
10355          * @param {String} value The URL
10356          */
10357         'url' : function(v){
10358             return url.test(v);
10359         },
10360         /**
10361          * The error text to display when the url validation function returns false
10362          * @type String
10363          */
10364         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10365         
10366         /**
10367          * The function used to validate alpha values
10368          * @param {String} value The value
10369          */
10370         'alpha' : function(v){
10371             return alpha.test(v);
10372         },
10373         /**
10374          * The error text to display when the alpha validation function returns false
10375          * @type String
10376          */
10377         'alphaText' : 'This field should only contain letters and _',
10378         /**
10379          * The keystroke filter mask to be applied on alpha input
10380          * @type RegExp
10381          */
10382         'alphaMask' : /[a-z_]/i,
10383
10384         /**
10385          * The function used to validate alphanumeric values
10386          * @param {String} value The value
10387          */
10388         'alphanum' : function(v){
10389             return alphanum.test(v);
10390         },
10391         /**
10392          * The error text to display when the alphanumeric validation function returns false
10393          * @type String
10394          */
10395         'alphanumText' : 'This field should only contain letters, numbers and _',
10396         /**
10397          * The keystroke filter mask to be applied on alphanumeric input
10398          * @type RegExp
10399          */
10400         'alphanumMask' : /[a-z0-9_]/i
10401     };
10402 }();/*
10403  * - LGPL
10404  *
10405  * Input
10406  * 
10407  */
10408
10409 /**
10410  * @class Roo.bootstrap.Input
10411  * @extends Roo.bootstrap.Component
10412  * Bootstrap Input class
10413  * @cfg {Boolean} disabled is it disabled
10414  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10415  * @cfg {String} name name of the input
10416  * @cfg {string} fieldLabel - the label associated
10417  * @cfg {string} placeholder - placeholder to put in text.
10418  * @cfg {string}  before - input group add on before
10419  * @cfg {string} after - input group add on after
10420  * @cfg {string} size - (lg|sm) or leave empty..
10421  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10422  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10423  * @cfg {Number} md colspan out of 12 for computer-sized screens
10424  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10425  * @cfg {string} value default value of the input
10426  * @cfg {Number} labelWidth set the width of label 
10427  * @cfg {Number} labellg set the width of label (1-12)
10428  * @cfg {Number} labelmd set the width of label (1-12)
10429  * @cfg {Number} labelsm set the width of label (1-12)
10430  * @cfg {Number} labelxs set the width of label (1-12)
10431  * @cfg {String} labelAlign (top|left)
10432  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10433  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10434  * @cfg {String} indicatorpos (left|right) default left
10435  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10436  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10437  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10438
10439  * @cfg {String} align (left|center|right) Default left
10440  * @cfg {Boolean} forceFeedback (true|false) Default false
10441  * 
10442  * @constructor
10443  * Create a new Input
10444  * @param {Object} config The config object
10445  */
10446
10447 Roo.bootstrap.Input = function(config){
10448     
10449     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10450     
10451     this.addEvents({
10452         /**
10453          * @event focus
10454          * Fires when this field receives input focus.
10455          * @param {Roo.form.Field} this
10456          */
10457         focus : true,
10458         /**
10459          * @event blur
10460          * Fires when this field loses input focus.
10461          * @param {Roo.form.Field} this
10462          */
10463         blur : true,
10464         /**
10465          * @event specialkey
10466          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10467          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10468          * @param {Roo.form.Field} this
10469          * @param {Roo.EventObject} e The event object
10470          */
10471         specialkey : true,
10472         /**
10473          * @event change
10474          * Fires just before the field blurs if the field value has changed.
10475          * @param {Roo.form.Field} this
10476          * @param {Mixed} newValue The new value
10477          * @param {Mixed} oldValue The original value
10478          */
10479         change : true,
10480         /**
10481          * @event invalid
10482          * Fires after the field has been marked as invalid.
10483          * @param {Roo.form.Field} this
10484          * @param {String} msg The validation message
10485          */
10486         invalid : true,
10487         /**
10488          * @event valid
10489          * Fires after the field has been validated with no errors.
10490          * @param {Roo.form.Field} this
10491          */
10492         valid : true,
10493          /**
10494          * @event keyup
10495          * Fires after the key up
10496          * @param {Roo.form.Field} this
10497          * @param {Roo.EventObject}  e The event Object
10498          */
10499         keyup : true
10500     });
10501 };
10502
10503 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10504      /**
10505      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10506       automatic validation (defaults to "keyup").
10507      */
10508     validationEvent : "keyup",
10509      /**
10510      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10511      */
10512     validateOnBlur : true,
10513     /**
10514      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10515      */
10516     validationDelay : 250,
10517      /**
10518      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10519      */
10520     focusClass : "x-form-focus",  // not needed???
10521     
10522        
10523     /**
10524      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10525      */
10526     invalidClass : "has-warning",
10527     
10528     /**
10529      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10530      */
10531     validClass : "has-success",
10532     
10533     /**
10534      * @cfg {Boolean} hasFeedback (true|false) default true
10535      */
10536     hasFeedback : true,
10537     
10538     /**
10539      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10540      */
10541     invalidFeedbackClass : "glyphicon-warning-sign",
10542     
10543     /**
10544      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10545      */
10546     validFeedbackClass : "glyphicon-ok",
10547     
10548     /**
10549      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10550      */
10551     selectOnFocus : false,
10552     
10553      /**
10554      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10555      */
10556     maskRe : null,
10557        /**
10558      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10559      */
10560     vtype : null,
10561     
10562       /**
10563      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10564      */
10565     disableKeyFilter : false,
10566     
10567        /**
10568      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10569      */
10570     disabled : false,
10571      /**
10572      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10573      */
10574     allowBlank : true,
10575     /**
10576      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10577      */
10578     blankText : "Please complete this mandatory field",
10579     
10580      /**
10581      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10582      */
10583     minLength : 0,
10584     /**
10585      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10586      */
10587     maxLength : Number.MAX_VALUE,
10588     /**
10589      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10590      */
10591     minLengthText : "The minimum length for this field is {0}",
10592     /**
10593      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10594      */
10595     maxLengthText : "The maximum length for this field is {0}",
10596   
10597     
10598     /**
10599      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10600      * If available, this function will be called only after the basic validators all return true, and will be passed the
10601      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10602      */
10603     validator : null,
10604     /**
10605      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10606      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10607      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10608      */
10609     regex : null,
10610     /**
10611      * @cfg {String} regexText -- Depricated - use Invalid Text
10612      */
10613     regexText : "",
10614     
10615     /**
10616      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10617      */
10618     invalidText : "",
10619     
10620     
10621     
10622     autocomplete: false,
10623     
10624     
10625     fieldLabel : '',
10626     inputType : 'text',
10627     
10628     name : false,
10629     placeholder: false,
10630     before : false,
10631     after : false,
10632     size : false,
10633     hasFocus : false,
10634     preventMark: false,
10635     isFormField : true,
10636     value : '',
10637     labelWidth : 2,
10638     labelAlign : false,
10639     readOnly : false,
10640     align : false,
10641     formatedValue : false,
10642     forceFeedback : false,
10643     
10644     indicatorpos : 'left',
10645     
10646     labellg : 0,
10647     labelmd : 0,
10648     labelsm : 0,
10649     labelxs : 0,
10650     
10651     capture : '',
10652     accept : '',
10653     
10654     parentLabelAlign : function()
10655     {
10656         var parent = this;
10657         while (parent.parent()) {
10658             parent = parent.parent();
10659             if (typeof(parent.labelAlign) !='undefined') {
10660                 return parent.labelAlign;
10661             }
10662         }
10663         return 'left';
10664         
10665     },
10666     
10667     getAutoCreate : function()
10668     {
10669         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10670         
10671         var id = Roo.id();
10672         
10673         var cfg = {};
10674         
10675         if(this.inputType != 'hidden'){
10676             cfg.cls = 'form-group' //input-group
10677         }
10678         
10679         var input =  {
10680             tag: 'input',
10681             id : id,
10682             type : this.inputType,
10683             value : this.value,
10684             cls : 'form-control',
10685             placeholder : this.placeholder || '',
10686             autocomplete : this.autocomplete || 'new-password'
10687         };
10688         if (this.inputType == 'file') {
10689             input.style = 'overflow:hidden'; // why not in CSS?
10690         }
10691         
10692         if(this.capture.length){
10693             input.capture = this.capture;
10694         }
10695         
10696         if(this.accept.length){
10697             input.accept = this.accept + "/*";
10698         }
10699         
10700         if(this.align){
10701             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10702         }
10703         
10704         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10705             input.maxLength = this.maxLength;
10706         }
10707         
10708         if (this.disabled) {
10709             input.disabled=true;
10710         }
10711         
10712         if (this.readOnly) {
10713             input.readonly=true;
10714         }
10715         
10716         if (this.name) {
10717             input.name = this.name;
10718         }
10719         
10720         if (this.size) {
10721             input.cls += ' input-' + this.size;
10722         }
10723         
10724         var settings=this;
10725         ['xs','sm','md','lg'].map(function(size){
10726             if (settings[size]) {
10727                 cfg.cls += ' col-' + size + '-' + settings[size];
10728             }
10729         });
10730         
10731         var inputblock = input;
10732         
10733         var feedback = {
10734             tag: 'span',
10735             cls: 'glyphicon form-control-feedback'
10736         };
10737             
10738         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10739             
10740             inputblock = {
10741                 cls : 'has-feedback',
10742                 cn :  [
10743                     input,
10744                     feedback
10745                 ] 
10746             };  
10747         }
10748         
10749         if (this.before || this.after) {
10750             
10751             inputblock = {
10752                 cls : 'input-group',
10753                 cn :  [] 
10754             };
10755             
10756             if (this.before && typeof(this.before) == 'string') {
10757                 
10758                 inputblock.cn.push({
10759                     tag :'span',
10760                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10761                     html : this.before
10762                 });
10763             }
10764             if (this.before && typeof(this.before) == 'object') {
10765                 this.before = Roo.factory(this.before);
10766                 
10767                 inputblock.cn.push({
10768                     tag :'span',
10769                     cls : 'roo-input-before input-group-prepend   input-group-' +
10770                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10771                 });
10772             }
10773             
10774             inputblock.cn.push(input);
10775             
10776             if (this.after && typeof(this.after) == 'string') {
10777                 inputblock.cn.push({
10778                     tag :'span',
10779                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10780                     html : this.after
10781                 });
10782             }
10783             if (this.after && typeof(this.after) == 'object') {
10784                 this.after = Roo.factory(this.after);
10785                 
10786                 inputblock.cn.push({
10787                     tag :'span',
10788                     cls : 'roo-input-after input-group-append  input-group-' +
10789                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10790                 });
10791             }
10792             
10793             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10794                 inputblock.cls += ' has-feedback';
10795                 inputblock.cn.push(feedback);
10796             }
10797         };
10798         var indicator = {
10799             tag : 'i',
10800             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10801             tooltip : 'This field is required'
10802         };
10803         if (this.allowBlank ) {
10804             indicator.style = this.allowBlank ? ' display:none' : '';
10805         }
10806         if (align ==='left' && this.fieldLabel.length) {
10807             
10808             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10809             
10810             cfg.cn = [
10811                 indicator,
10812                 {
10813                     tag: 'label',
10814                     'for' :  id,
10815                     cls : 'control-label col-form-label',
10816                     html : this.fieldLabel
10817
10818                 },
10819                 {
10820                     cls : "", 
10821                     cn: [
10822                         inputblock
10823                     ]
10824                 }
10825             ];
10826             
10827             var labelCfg = cfg.cn[1];
10828             var contentCfg = cfg.cn[2];
10829             
10830             if(this.indicatorpos == 'right'){
10831                 cfg.cn = [
10832                     {
10833                         tag: 'label',
10834                         'for' :  id,
10835                         cls : 'control-label col-form-label',
10836                         cn : [
10837                             {
10838                                 tag : 'span',
10839                                 html : this.fieldLabel
10840                             },
10841                             indicator
10842                         ]
10843                     },
10844                     {
10845                         cls : "",
10846                         cn: [
10847                             inputblock
10848                         ]
10849                     }
10850
10851                 ];
10852                 
10853                 labelCfg = cfg.cn[0];
10854                 contentCfg = cfg.cn[1];
10855             
10856             }
10857             
10858             if(this.labelWidth > 12){
10859                 labelCfg.style = "width: " + this.labelWidth + 'px';
10860             }
10861             
10862             if(this.labelWidth < 13 && this.labelmd == 0){
10863                 this.labelmd = this.labelWidth;
10864             }
10865             
10866             if(this.labellg > 0){
10867                 labelCfg.cls += ' col-lg-' + this.labellg;
10868                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10869             }
10870             
10871             if(this.labelmd > 0){
10872                 labelCfg.cls += ' col-md-' + this.labelmd;
10873                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10874             }
10875             
10876             if(this.labelsm > 0){
10877                 labelCfg.cls += ' col-sm-' + this.labelsm;
10878                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10879             }
10880             
10881             if(this.labelxs > 0){
10882                 labelCfg.cls += ' col-xs-' + this.labelxs;
10883                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10884             }
10885             
10886             
10887         } else if ( this.fieldLabel.length) {
10888                 
10889             
10890             
10891             cfg.cn = [
10892                 {
10893                     tag : 'i',
10894                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10895                     tooltip : 'This field is required',
10896                     style : this.allowBlank ? ' display:none' : '' 
10897                 },
10898                 {
10899                     tag: 'label',
10900                    //cls : 'input-group-addon',
10901                     html : this.fieldLabel
10902
10903                 },
10904
10905                inputblock
10906
10907            ];
10908            
10909            if(this.indicatorpos == 'right'){
10910        
10911                 cfg.cn = [
10912                     {
10913                         tag: 'label',
10914                        //cls : 'input-group-addon',
10915                         html : this.fieldLabel
10916
10917                     },
10918                     {
10919                         tag : 'i',
10920                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10921                         tooltip : 'This field is required',
10922                         style : this.allowBlank ? ' display:none' : '' 
10923                     },
10924
10925                    inputblock
10926
10927                ];
10928
10929             }
10930
10931         } else {
10932             
10933             cfg.cn = [
10934
10935                     inputblock
10936
10937             ];
10938                 
10939                 
10940         };
10941         
10942         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10943            cfg.cls += ' navbar-form';
10944         }
10945         
10946         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10947             // on BS4 we do this only if not form 
10948             cfg.cls += ' navbar-form';
10949             cfg.tag = 'li';
10950         }
10951         
10952         return cfg;
10953         
10954     },
10955     /**
10956      * return the real input element.
10957      */
10958     inputEl: function ()
10959     {
10960         return this.el.select('input.form-control',true).first();
10961     },
10962     
10963     tooltipEl : function()
10964     {
10965         return this.inputEl();
10966     },
10967     
10968     indicatorEl : function()
10969     {
10970         if (Roo.bootstrap.version == 4) {
10971             return false; // not enabled in v4 yet.
10972         }
10973         
10974         var indicator = this.el.select('i.roo-required-indicator',true).first();
10975         
10976         if(!indicator){
10977             return false;
10978         }
10979         
10980         return indicator;
10981         
10982     },
10983     
10984     setDisabled : function(v)
10985     {
10986         var i  = this.inputEl().dom;
10987         if (!v) {
10988             i.removeAttribute('disabled');
10989             return;
10990             
10991         }
10992         i.setAttribute('disabled','true');
10993     },
10994     initEvents : function()
10995     {
10996           
10997         this.inputEl().on("keydown" , this.fireKey,  this);
10998         this.inputEl().on("focus", this.onFocus,  this);
10999         this.inputEl().on("blur", this.onBlur,  this);
11000         
11001         this.inputEl().relayEvent('keyup', this);
11002         
11003         this.indicator = this.indicatorEl();
11004         
11005         if(this.indicator){
11006             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
11007         }
11008  
11009         // reference to original value for reset
11010         this.originalValue = this.getValue();
11011         //Roo.form.TextField.superclass.initEvents.call(this);
11012         if(this.validationEvent == 'keyup'){
11013             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11014             this.inputEl().on('keyup', this.filterValidation, this);
11015         }
11016         else if(this.validationEvent !== false){
11017             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11018         }
11019         
11020         if(this.selectOnFocus){
11021             this.on("focus", this.preFocus, this);
11022             
11023         }
11024         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11025             this.inputEl().on("keypress", this.filterKeys, this);
11026         } else {
11027             this.inputEl().relayEvent('keypress', this);
11028         }
11029        /* if(this.grow){
11030             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11031             this.el.on("click", this.autoSize,  this);
11032         }
11033         */
11034         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11035             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11036         }
11037         
11038         if (typeof(this.before) == 'object') {
11039             this.before.render(this.el.select('.roo-input-before',true).first());
11040         }
11041         if (typeof(this.after) == 'object') {
11042             this.after.render(this.el.select('.roo-input-after',true).first());
11043         }
11044         
11045         this.inputEl().on('change', this.onChange, this);
11046         
11047     },
11048     filterValidation : function(e){
11049         if(!e.isNavKeyPress()){
11050             this.validationTask.delay(this.validationDelay);
11051         }
11052     },
11053      /**
11054      * Validates the field value
11055      * @return {Boolean} True if the value is valid, else false
11056      */
11057     validate : function(){
11058         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11059         if(this.disabled || this.validateValue(this.getRawValue())){
11060             this.markValid();
11061             return true;
11062         }
11063         
11064         this.markInvalid();
11065         return false;
11066     },
11067     
11068     
11069     /**
11070      * Validates a value according to the field's validation rules and marks the field as invalid
11071      * if the validation fails
11072      * @param {Mixed} value The value to validate
11073      * @return {Boolean} True if the value is valid, else false
11074      */
11075     validateValue : function(value)
11076     {
11077         if(this.getVisibilityEl().hasClass('hidden')){
11078             return true;
11079         }
11080         
11081         if(value.length < 1)  { // if it's blank
11082             if(this.allowBlank){
11083                 return true;
11084             }
11085             return false;
11086         }
11087         
11088         if(value.length < this.minLength){
11089             return false;
11090         }
11091         if(value.length > this.maxLength){
11092             return false;
11093         }
11094         if(this.vtype){
11095             var vt = Roo.form.VTypes;
11096             if(!vt[this.vtype](value, this)){
11097                 return false;
11098             }
11099         }
11100         if(typeof this.validator == "function"){
11101             var msg = this.validator(value);
11102             if(msg !== true){
11103                 return false;
11104             }
11105             if (typeof(msg) == 'string') {
11106                 this.invalidText = msg;
11107             }
11108         }
11109         
11110         if(this.regex && !this.regex.test(value)){
11111             return false;
11112         }
11113         
11114         return true;
11115     },
11116     
11117      // private
11118     fireKey : function(e){
11119         //Roo.log('field ' + e.getKey());
11120         if(e.isNavKeyPress()){
11121             this.fireEvent("specialkey", this, e);
11122         }
11123     },
11124     focus : function (selectText){
11125         if(this.rendered){
11126             this.inputEl().focus();
11127             if(selectText === true){
11128                 this.inputEl().dom.select();
11129             }
11130         }
11131         return this;
11132     } ,
11133     
11134     onFocus : function(){
11135         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11136            // this.el.addClass(this.focusClass);
11137         }
11138         if(!this.hasFocus){
11139             this.hasFocus = true;
11140             this.startValue = this.getValue();
11141             this.fireEvent("focus", this);
11142         }
11143     },
11144     
11145     beforeBlur : Roo.emptyFn,
11146
11147     
11148     // private
11149     onBlur : function(){
11150         this.beforeBlur();
11151         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11152             //this.el.removeClass(this.focusClass);
11153         }
11154         this.hasFocus = false;
11155         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11156             this.validate();
11157         }
11158         var v = this.getValue();
11159         if(String(v) !== String(this.startValue)){
11160             this.fireEvent('change', this, v, this.startValue);
11161         }
11162         this.fireEvent("blur", this);
11163     },
11164     
11165     onChange : function(e)
11166     {
11167         var v = this.getValue();
11168         if(String(v) !== String(this.startValue)){
11169             this.fireEvent('change', this, v, this.startValue);
11170         }
11171         
11172     },
11173     
11174     /**
11175      * Resets the current field value to the originally loaded value and clears any validation messages
11176      */
11177     reset : function(){
11178         this.setValue(this.originalValue);
11179         this.validate();
11180     },
11181      /**
11182      * Returns the name of the field
11183      * @return {Mixed} name The name field
11184      */
11185     getName: function(){
11186         return this.name;
11187     },
11188      /**
11189      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11190      * @return {Mixed} value The field value
11191      */
11192     getValue : function(){
11193         
11194         var v = this.inputEl().getValue();
11195         
11196         return v;
11197     },
11198     /**
11199      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11200      * @return {Mixed} value The field value
11201      */
11202     getRawValue : function(){
11203         var v = this.inputEl().getValue();
11204         
11205         return v;
11206     },
11207     
11208     /**
11209      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11210      * @param {Mixed} value The value to set
11211      */
11212     setRawValue : function(v){
11213         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11214     },
11215     
11216     selectText : function(start, end){
11217         var v = this.getRawValue();
11218         if(v.length > 0){
11219             start = start === undefined ? 0 : start;
11220             end = end === undefined ? v.length : end;
11221             var d = this.inputEl().dom;
11222             if(d.setSelectionRange){
11223                 d.setSelectionRange(start, end);
11224             }else if(d.createTextRange){
11225                 var range = d.createTextRange();
11226                 range.moveStart("character", start);
11227                 range.moveEnd("character", v.length-end);
11228                 range.select();
11229             }
11230         }
11231     },
11232     
11233     /**
11234      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11235      * @param {Mixed} value The value to set
11236      */
11237     setValue : function(v){
11238         this.value = v;
11239         if(this.rendered){
11240             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11241             this.validate();
11242         }
11243     },
11244     
11245     /*
11246     processValue : function(value){
11247         if(this.stripCharsRe){
11248             var newValue = value.replace(this.stripCharsRe, '');
11249             if(newValue !== value){
11250                 this.setRawValue(newValue);
11251                 return newValue;
11252             }
11253         }
11254         return value;
11255     },
11256   */
11257     preFocus : function(){
11258         
11259         if(this.selectOnFocus){
11260             this.inputEl().dom.select();
11261         }
11262     },
11263     filterKeys : function(e){
11264         var k = e.getKey();
11265         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11266             return;
11267         }
11268         var c = e.getCharCode(), cc = String.fromCharCode(c);
11269         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11270             return;
11271         }
11272         if(!this.maskRe.test(cc)){
11273             e.stopEvent();
11274         }
11275     },
11276      /**
11277      * Clear any invalid styles/messages for this field
11278      */
11279     clearInvalid : function(){
11280         
11281         if(!this.el || this.preventMark){ // not rendered
11282             return;
11283         }
11284         
11285         
11286         this.el.removeClass([this.invalidClass, 'is-invalid']);
11287         
11288         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11289             
11290             var feedback = this.el.select('.form-control-feedback', true).first();
11291             
11292             if(feedback){
11293                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11294             }
11295             
11296         }
11297         
11298         if(this.indicator){
11299             this.indicator.removeClass('visible');
11300             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11301         }
11302         
11303         this.fireEvent('valid', this);
11304     },
11305     
11306      /**
11307      * Mark this field as valid
11308      */
11309     markValid : function()
11310     {
11311         if(!this.el  || this.preventMark){ // not rendered...
11312             return;
11313         }
11314         
11315         this.el.removeClass([this.invalidClass, this.validClass]);
11316         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11317
11318         var feedback = this.el.select('.form-control-feedback', true).first();
11319             
11320         if(feedback){
11321             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11322         }
11323         
11324         if(this.indicator){
11325             this.indicator.removeClass('visible');
11326             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11327         }
11328         
11329         if(this.disabled){
11330             return;
11331         }
11332         
11333            
11334         if(this.allowBlank && !this.getRawValue().length){
11335             return;
11336         }
11337         if (Roo.bootstrap.version == 3) {
11338             this.el.addClass(this.validClass);
11339         } else {
11340             this.inputEl().addClass('is-valid');
11341         }
11342
11343         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11344             
11345             var feedback = this.el.select('.form-control-feedback', true).first();
11346             
11347             if(feedback){
11348                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11349                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11350             }
11351             
11352         }
11353         
11354         this.fireEvent('valid', this);
11355     },
11356     
11357      /**
11358      * Mark this field as invalid
11359      * @param {String} msg The validation message
11360      */
11361     markInvalid : function(msg)
11362     {
11363         if(!this.el  || this.preventMark){ // not rendered
11364             return;
11365         }
11366         
11367         this.el.removeClass([this.invalidClass, this.validClass]);
11368         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11369         
11370         var feedback = this.el.select('.form-control-feedback', true).first();
11371             
11372         if(feedback){
11373             this.el.select('.form-control-feedback', true).first().removeClass(
11374                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11375         }
11376
11377         if(this.disabled){
11378             return;
11379         }
11380         
11381         if(this.allowBlank && !this.getRawValue().length){
11382             return;
11383         }
11384         
11385         if(this.indicator){
11386             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11387             this.indicator.addClass('visible');
11388         }
11389         if (Roo.bootstrap.version == 3) {
11390             this.el.addClass(this.invalidClass);
11391         } else {
11392             this.inputEl().addClass('is-invalid');
11393         }
11394         
11395         
11396         
11397         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11398             
11399             var feedback = this.el.select('.form-control-feedback', true).first();
11400             
11401             if(feedback){
11402                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11403                 
11404                 if(this.getValue().length || this.forceFeedback){
11405                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11406                 }
11407                 
11408             }
11409             
11410         }
11411         
11412         this.fireEvent('invalid', this, msg);
11413     },
11414     // private
11415     SafariOnKeyDown : function(event)
11416     {
11417         // this is a workaround for a password hang bug on chrome/ webkit.
11418         if (this.inputEl().dom.type != 'password') {
11419             return;
11420         }
11421         
11422         var isSelectAll = false;
11423         
11424         if(this.inputEl().dom.selectionEnd > 0){
11425             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11426         }
11427         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11428             event.preventDefault();
11429             this.setValue('');
11430             return;
11431         }
11432         
11433         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11434             
11435             event.preventDefault();
11436             // this is very hacky as keydown always get's upper case.
11437             //
11438             var cc = String.fromCharCode(event.getCharCode());
11439             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11440             
11441         }
11442     },
11443     adjustWidth : function(tag, w){
11444         tag = tag.toLowerCase();
11445         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11446             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11447                 if(tag == 'input'){
11448                     return w + 2;
11449                 }
11450                 if(tag == 'textarea'){
11451                     return w-2;
11452                 }
11453             }else if(Roo.isOpera){
11454                 if(tag == 'input'){
11455                     return w + 2;
11456                 }
11457                 if(tag == 'textarea'){
11458                     return w-2;
11459                 }
11460             }
11461         }
11462         return w;
11463     },
11464     
11465     setFieldLabel : function(v)
11466     {
11467         if(!this.rendered){
11468             return;
11469         }
11470         
11471         if(this.indicatorEl()){
11472             var ar = this.el.select('label > span',true);
11473             
11474             if (ar.elements.length) {
11475                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             var br = this.el.select('label',true);
11481             
11482             if(br.elements.length) {
11483                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11484                 this.fieldLabel = v;
11485                 return;
11486             }
11487             
11488             Roo.log('Cannot Found any of label > span || label in input');
11489             return;
11490         }
11491         
11492         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11493         this.fieldLabel = v;
11494         
11495         
11496     }
11497 });
11498
11499  
11500 /*
11501  * - LGPL
11502  *
11503  * Input
11504  * 
11505  */
11506
11507 /**
11508  * @class Roo.bootstrap.TextArea
11509  * @extends Roo.bootstrap.Input
11510  * Bootstrap TextArea class
11511  * @cfg {Number} cols Specifies the visible width of a text area
11512  * @cfg {Number} rows Specifies the visible number of lines in a text area
11513  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11514  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11515  * @cfg {string} html text
11516  * 
11517  * @constructor
11518  * Create a new TextArea
11519  * @param {Object} config The config object
11520  */
11521
11522 Roo.bootstrap.TextArea = function(config){
11523     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11524    
11525 };
11526
11527 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11528      
11529     cols : false,
11530     rows : 5,
11531     readOnly : false,
11532     warp : 'soft',
11533     resize : false,
11534     value: false,
11535     html: false,
11536     
11537     getAutoCreate : function(){
11538         
11539         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11540         
11541         var id = Roo.id();
11542         
11543         var cfg = {};
11544         
11545         if(this.inputType != 'hidden'){
11546             cfg.cls = 'form-group' //input-group
11547         }
11548         
11549         var input =  {
11550             tag: 'textarea',
11551             id : id,
11552             warp : this.warp,
11553             rows : this.rows,
11554             value : this.value || '',
11555             html: this.html || '',
11556             cls : 'form-control',
11557             placeholder : this.placeholder || '' 
11558             
11559         };
11560         
11561         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11562             input.maxLength = this.maxLength;
11563         }
11564         
11565         if(this.resize){
11566             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11567         }
11568         
11569         if(this.cols){
11570             input.cols = this.cols;
11571         }
11572         
11573         if (this.readOnly) {
11574             input.readonly = true;
11575         }
11576         
11577         if (this.name) {
11578             input.name = this.name;
11579         }
11580         
11581         if (this.size) {
11582             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11583         }
11584         
11585         var settings=this;
11586         ['xs','sm','md','lg'].map(function(size){
11587             if (settings[size]) {
11588                 cfg.cls += ' col-' + size + '-' + settings[size];
11589             }
11590         });
11591         
11592         var inputblock = input;
11593         
11594         if(this.hasFeedback && !this.allowBlank){
11595             
11596             var feedback = {
11597                 tag: 'span',
11598                 cls: 'glyphicon form-control-feedback'
11599             };
11600
11601             inputblock = {
11602                 cls : 'has-feedback',
11603                 cn :  [
11604                     input,
11605                     feedback
11606                 ] 
11607             };  
11608         }
11609         
11610         
11611         if (this.before || this.after) {
11612             
11613             inputblock = {
11614                 cls : 'input-group',
11615                 cn :  [] 
11616             };
11617             if (this.before) {
11618                 inputblock.cn.push({
11619                     tag :'span',
11620                     cls : 'input-group-addon',
11621                     html : this.before
11622                 });
11623             }
11624             
11625             inputblock.cn.push(input);
11626             
11627             if(this.hasFeedback && !this.allowBlank){
11628                 inputblock.cls += ' has-feedback';
11629                 inputblock.cn.push(feedback);
11630             }
11631             
11632             if (this.after) {
11633                 inputblock.cn.push({
11634                     tag :'span',
11635                     cls : 'input-group-addon',
11636                     html : this.after
11637                 });
11638             }
11639             
11640         }
11641         
11642         if (align ==='left' && this.fieldLabel.length) {
11643             cfg.cn = [
11644                 {
11645                     tag: 'label',
11646                     'for' :  id,
11647                     cls : 'control-label',
11648                     html : this.fieldLabel
11649                 },
11650                 {
11651                     cls : "",
11652                     cn: [
11653                         inputblock
11654                     ]
11655                 }
11656
11657             ];
11658             
11659             if(this.labelWidth > 12){
11660                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11661             }
11662
11663             if(this.labelWidth < 13 && this.labelmd == 0){
11664                 this.labelmd = this.labelWidth;
11665             }
11666
11667             if(this.labellg > 0){
11668                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11669                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11670             }
11671
11672             if(this.labelmd > 0){
11673                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11674                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11675             }
11676
11677             if(this.labelsm > 0){
11678                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11679                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11680             }
11681
11682             if(this.labelxs > 0){
11683                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11684                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11685             }
11686             
11687         } else if ( this.fieldLabel.length) {
11688             cfg.cn = [
11689
11690                {
11691                    tag: 'label',
11692                    //cls : 'input-group-addon',
11693                    html : this.fieldLabel
11694
11695                },
11696
11697                inputblock
11698
11699            ];
11700
11701         } else {
11702
11703             cfg.cn = [
11704
11705                 inputblock
11706
11707             ];
11708                 
11709         }
11710         
11711         if (this.disabled) {
11712             input.disabled=true;
11713         }
11714         
11715         return cfg;
11716         
11717     },
11718     /**
11719      * return the real textarea element.
11720      */
11721     inputEl: function ()
11722     {
11723         return this.el.select('textarea.form-control',true).first();
11724     },
11725     
11726     /**
11727      * Clear any invalid styles/messages for this field
11728      */
11729     clearInvalid : function()
11730     {
11731         
11732         if(!this.el || this.preventMark){ // not rendered
11733             return;
11734         }
11735         
11736         var label = this.el.select('label', true).first();
11737         var icon = this.el.select('i.fa-star', true).first();
11738         
11739         if(label && icon){
11740             icon.remove();
11741         }
11742         this.el.removeClass( this.validClass);
11743         this.inputEl().removeClass('is-invalid');
11744          
11745         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11746             
11747             var feedback = this.el.select('.form-control-feedback', true).first();
11748             
11749             if(feedback){
11750                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11751             }
11752             
11753         }
11754         
11755         this.fireEvent('valid', this);
11756     },
11757     
11758      /**
11759      * Mark this field as valid
11760      */
11761     markValid : function()
11762     {
11763         if(!this.el  || this.preventMark){ // not rendered
11764             return;
11765         }
11766         
11767         this.el.removeClass([this.invalidClass, this.validClass]);
11768         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11769         
11770         var feedback = this.el.select('.form-control-feedback', true).first();
11771             
11772         if(feedback){
11773             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11774         }
11775
11776         if(this.disabled || this.allowBlank){
11777             return;
11778         }
11779         
11780         var label = this.el.select('label', true).first();
11781         var icon = this.el.select('i.fa-star', true).first();
11782         
11783         if(label && icon){
11784             icon.remove();
11785         }
11786         if (Roo.bootstrap.version == 3) {
11787             this.el.addClass(this.validClass);
11788         } else {
11789             this.inputEl().addClass('is-valid');
11790         }
11791         
11792         
11793         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11794             
11795             var feedback = this.el.select('.form-control-feedback', true).first();
11796             
11797             if(feedback){
11798                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11799                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11800             }
11801             
11802         }
11803         
11804         this.fireEvent('valid', this);
11805     },
11806     
11807      /**
11808      * Mark this field as invalid
11809      * @param {String} msg The validation message
11810      */
11811     markInvalid : function(msg)
11812     {
11813         if(!this.el  || this.preventMark){ // not rendered
11814             return;
11815         }
11816         
11817         this.el.removeClass([this.invalidClass, this.validClass]);
11818         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11819         
11820         var feedback = this.el.select('.form-control-feedback', true).first();
11821             
11822         if(feedback){
11823             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11824         }
11825
11826         if(this.disabled || this.allowBlank){
11827             return;
11828         }
11829         
11830         var label = this.el.select('label', true).first();
11831         var icon = this.el.select('i.fa-star', true).first();
11832         
11833         if(!this.getValue().length && label && !icon){
11834             this.el.createChild({
11835                 tag : 'i',
11836                 cls : 'text-danger fa fa-lg fa-star',
11837                 tooltip : 'This field is required',
11838                 style : 'margin-right:5px;'
11839             }, label, true);
11840         }
11841         
11842         if (Roo.bootstrap.version == 3) {
11843             this.el.addClass(this.invalidClass);
11844         } else {
11845             this.inputEl().addClass('is-invalid');
11846         }
11847         
11848         // fixme ... this may be depricated need to test..
11849         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11850             
11851             var feedback = this.el.select('.form-control-feedback', true).first();
11852             
11853             if(feedback){
11854                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11855                 
11856                 if(this.getValue().length || this.forceFeedback){
11857                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11858                 }
11859                 
11860             }
11861             
11862         }
11863         
11864         this.fireEvent('invalid', this, msg);
11865     }
11866 });
11867
11868  
11869 /*
11870  * - LGPL
11871  *
11872  * trigger field - base class for combo..
11873  * 
11874  */
11875  
11876 /**
11877  * @class Roo.bootstrap.TriggerField
11878  * @extends Roo.bootstrap.Input
11879  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11880  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11881  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11882  * for which you can provide a custom implementation.  For example:
11883  * <pre><code>
11884 var trigger = new Roo.bootstrap.TriggerField();
11885 trigger.onTriggerClick = myTriggerFn;
11886 trigger.applyTo('my-field');
11887 </code></pre>
11888  *
11889  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11890  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11891  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11892  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11893  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11894
11895  * @constructor
11896  * Create a new TriggerField.
11897  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11898  * to the base TextField)
11899  */
11900 Roo.bootstrap.TriggerField = function(config){
11901     this.mimicing = false;
11902     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11903 };
11904
11905 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11906     /**
11907      * @cfg {String} triggerClass A CSS class to apply to the trigger
11908      */
11909      /**
11910      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11911      */
11912     hideTrigger:false,
11913
11914     /**
11915      * @cfg {Boolean} removable (true|false) special filter default false
11916      */
11917     removable : false,
11918     
11919     /** @cfg {Boolean} grow @hide */
11920     /** @cfg {Number} growMin @hide */
11921     /** @cfg {Number} growMax @hide */
11922
11923     /**
11924      * @hide 
11925      * @method
11926      */
11927     autoSize: Roo.emptyFn,
11928     // private
11929     monitorTab : true,
11930     // private
11931     deferHeight : true,
11932
11933     
11934     actionMode : 'wrap',
11935     
11936     caret : false,
11937     
11938     
11939     getAutoCreate : function(){
11940        
11941         var align = this.labelAlign || this.parentLabelAlign();
11942         
11943         var id = Roo.id();
11944         
11945         var cfg = {
11946             cls: 'form-group' //input-group
11947         };
11948         
11949         
11950         var input =  {
11951             tag: 'input',
11952             id : id,
11953             type : this.inputType,
11954             cls : 'form-control',
11955             autocomplete: 'new-password',
11956             placeholder : this.placeholder || '' 
11957             
11958         };
11959         if (this.name) {
11960             input.name = this.name;
11961         }
11962         if (this.size) {
11963             input.cls += ' input-' + this.size;
11964         }
11965         
11966         if (this.disabled) {
11967             input.disabled=true;
11968         }
11969         
11970         var inputblock = input;
11971         
11972         if(this.hasFeedback && !this.allowBlank){
11973             
11974             var feedback = {
11975                 tag: 'span',
11976                 cls: 'glyphicon form-control-feedback'
11977             };
11978             
11979             if(this.removable && !this.editable  ){
11980                 inputblock = {
11981                     cls : 'has-feedback',
11982                     cn :  [
11983                         inputblock,
11984                         {
11985                             tag: 'button',
11986                             html : 'x',
11987                             cls : 'roo-combo-removable-btn close'
11988                         },
11989                         feedback
11990                     ] 
11991                 };
11992             } else {
11993                 inputblock = {
11994                     cls : 'has-feedback',
11995                     cn :  [
11996                         inputblock,
11997                         feedback
11998                     ] 
11999                 };
12000             }
12001
12002         } else {
12003             if(this.removable && !this.editable ){
12004                 inputblock = {
12005                     cls : 'roo-removable',
12006                     cn :  [
12007                         inputblock,
12008                         {
12009                             tag: 'button',
12010                             html : 'x',
12011                             cls : 'roo-combo-removable-btn close'
12012                         }
12013                     ] 
12014                 };
12015             }
12016         }
12017         
12018         if (this.before || this.after) {
12019             
12020             inputblock = {
12021                 cls : 'input-group',
12022                 cn :  [] 
12023             };
12024             if (this.before) {
12025                 inputblock.cn.push({
12026                     tag :'span',
12027                     cls : 'input-group-addon input-group-prepend input-group-text',
12028                     html : this.before
12029                 });
12030             }
12031             
12032             inputblock.cn.push(input);
12033             
12034             if(this.hasFeedback && !this.allowBlank){
12035                 inputblock.cls += ' has-feedback';
12036                 inputblock.cn.push(feedback);
12037             }
12038             
12039             if (this.after) {
12040                 inputblock.cn.push({
12041                     tag :'span',
12042                     cls : 'input-group-addon input-group-append input-group-text',
12043                     html : this.after
12044                 });
12045             }
12046             
12047         };
12048         
12049       
12050         
12051         var ibwrap = inputblock;
12052         
12053         if(this.multiple){
12054             ibwrap = {
12055                 tag: 'ul',
12056                 cls: 'roo-select2-choices',
12057                 cn:[
12058                     {
12059                         tag: 'li',
12060                         cls: 'roo-select2-search-field',
12061                         cn: [
12062
12063                             inputblock
12064                         ]
12065                     }
12066                 ]
12067             };
12068                 
12069         }
12070         
12071         var combobox = {
12072             cls: 'roo-select2-container input-group',
12073             cn: [
12074                  {
12075                     tag: 'input',
12076                     type : 'hidden',
12077                     cls: 'form-hidden-field'
12078                 },
12079                 ibwrap
12080             ]
12081         };
12082         
12083         if(!this.multiple && this.showToggleBtn){
12084             
12085             var caret = {
12086                         tag: 'span',
12087                         cls: 'caret'
12088              };
12089             if (this.caret != false) {
12090                 caret = {
12091                      tag: 'i',
12092                      cls: 'fa fa-' + this.caret
12093                 };
12094                 
12095             }
12096             
12097             combobox.cn.push({
12098                 tag :'span',
12099                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12100                 cn : [
12101                     Roo.bootstrap.version == 3 ? caret : '',
12102                     {
12103                         tag: 'span',
12104                         cls: 'combobox-clear',
12105                         cn  : [
12106                             {
12107                                 tag : 'i',
12108                                 cls: 'icon-remove'
12109                             }
12110                         ]
12111                     }
12112                 ]
12113
12114             })
12115         }
12116         
12117         if(this.multiple){
12118             combobox.cls += ' roo-select2-container-multi';
12119         }
12120          var indicator = {
12121             tag : 'i',
12122             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12123             tooltip : 'This field is required'
12124         };
12125         if (Roo.bootstrap.version == 4) {
12126             indicator = {
12127                 tag : 'i',
12128                 style : 'display:none'
12129             };
12130         }
12131         
12132         
12133         if (align ==='left' && this.fieldLabel.length) {
12134             
12135             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12136
12137             cfg.cn = [
12138                 indicator,
12139                 {
12140                     tag: 'label',
12141                     'for' :  id,
12142                     cls : 'control-label',
12143                     html : this.fieldLabel
12144
12145                 },
12146                 {
12147                     cls : "", 
12148                     cn: [
12149                         combobox
12150                     ]
12151                 }
12152
12153             ];
12154             
12155             var labelCfg = cfg.cn[1];
12156             var contentCfg = cfg.cn[2];
12157             
12158             if(this.indicatorpos == 'right'){
12159                 cfg.cn = [
12160                     {
12161                         tag: 'label',
12162                         'for' :  id,
12163                         cls : 'control-label',
12164                         cn : [
12165                             {
12166                                 tag : 'span',
12167                                 html : this.fieldLabel
12168                             },
12169                             indicator
12170                         ]
12171                     },
12172                     {
12173                         cls : "", 
12174                         cn: [
12175                             combobox
12176                         ]
12177                     }
12178
12179                 ];
12180                 
12181                 labelCfg = cfg.cn[0];
12182                 contentCfg = cfg.cn[1];
12183             }
12184             
12185             if(this.labelWidth > 12){
12186                 labelCfg.style = "width: " + this.labelWidth + 'px';
12187             }
12188             
12189             if(this.labelWidth < 13 && this.labelmd == 0){
12190                 this.labelmd = this.labelWidth;
12191             }
12192             
12193             if(this.labellg > 0){
12194                 labelCfg.cls += ' col-lg-' + this.labellg;
12195                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12196             }
12197             
12198             if(this.labelmd > 0){
12199                 labelCfg.cls += ' col-md-' + this.labelmd;
12200                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12201             }
12202             
12203             if(this.labelsm > 0){
12204                 labelCfg.cls += ' col-sm-' + this.labelsm;
12205                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12206             }
12207             
12208             if(this.labelxs > 0){
12209                 labelCfg.cls += ' col-xs-' + this.labelxs;
12210                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12211             }
12212             
12213         } else if ( this.fieldLabel.length) {
12214 //                Roo.log(" label");
12215             cfg.cn = [
12216                 indicator,
12217                {
12218                    tag: 'label',
12219                    //cls : 'input-group-addon',
12220                    html : this.fieldLabel
12221
12222                },
12223
12224                combobox
12225
12226             ];
12227             
12228             if(this.indicatorpos == 'right'){
12229                 
12230                 cfg.cn = [
12231                     {
12232                        tag: 'label',
12233                        cn : [
12234                            {
12235                                tag : 'span',
12236                                html : this.fieldLabel
12237                            },
12238                            indicator
12239                        ]
12240
12241                     },
12242                     combobox
12243
12244                 ];
12245
12246             }
12247
12248         } else {
12249             
12250 //                Roo.log(" no label && no align");
12251                 cfg = combobox
12252                      
12253                 
12254         }
12255         
12256         var settings=this;
12257         ['xs','sm','md','lg'].map(function(size){
12258             if (settings[size]) {
12259                 cfg.cls += ' col-' + size + '-' + settings[size];
12260             }
12261         });
12262         
12263         return cfg;
12264         
12265     },
12266     
12267     
12268     
12269     // private
12270     onResize : function(w, h){
12271 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12272 //        if(typeof w == 'number'){
12273 //            var x = w - this.trigger.getWidth();
12274 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12275 //            this.trigger.setStyle('left', x+'px');
12276 //        }
12277     },
12278
12279     // private
12280     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12281
12282     // private
12283     getResizeEl : function(){
12284         return this.inputEl();
12285     },
12286
12287     // private
12288     getPositionEl : function(){
12289         return this.inputEl();
12290     },
12291
12292     // private
12293     alignErrorIcon : function(){
12294         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12295     },
12296
12297     // private
12298     initEvents : function(){
12299         
12300         this.createList();
12301         
12302         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12303         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12304         if(!this.multiple && this.showToggleBtn){
12305             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12306             if(this.hideTrigger){
12307                 this.trigger.setDisplayed(false);
12308             }
12309             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12310         }
12311         
12312         if(this.multiple){
12313             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12314         }
12315         
12316         if(this.removable && !this.editable && !this.tickable){
12317             var close = this.closeTriggerEl();
12318             
12319             if(close){
12320                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12321                 close.on('click', this.removeBtnClick, this, close);
12322             }
12323         }
12324         
12325         //this.trigger.addClassOnOver('x-form-trigger-over');
12326         //this.trigger.addClassOnClick('x-form-trigger-click');
12327         
12328         //if(!this.width){
12329         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12330         //}
12331     },
12332     
12333     closeTriggerEl : function()
12334     {
12335         var close = this.el.select('.roo-combo-removable-btn', true).first();
12336         return close ? close : false;
12337     },
12338     
12339     removeBtnClick : function(e, h, el)
12340     {
12341         e.preventDefault();
12342         
12343         if(this.fireEvent("remove", this) !== false){
12344             this.reset();
12345             this.fireEvent("afterremove", this)
12346         }
12347     },
12348     
12349     createList : function()
12350     {
12351         this.list = Roo.get(document.body).createChild({
12352             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12353             cls: 'typeahead typeahead-long dropdown-menu shadow',
12354             style: 'display:none'
12355         });
12356         
12357         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12358         
12359     },
12360
12361     // private
12362     initTrigger : function(){
12363        
12364     },
12365
12366     // private
12367     onDestroy : function(){
12368         if(this.trigger){
12369             this.trigger.removeAllListeners();
12370           //  this.trigger.remove();
12371         }
12372         //if(this.wrap){
12373         //    this.wrap.remove();
12374         //}
12375         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12376     },
12377
12378     // private
12379     onFocus : function(){
12380         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12381         /*
12382         if(!this.mimicing){
12383             this.wrap.addClass('x-trigger-wrap-focus');
12384             this.mimicing = true;
12385             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12386             if(this.monitorTab){
12387                 this.el.on("keydown", this.checkTab, this);
12388             }
12389         }
12390         */
12391     },
12392
12393     // private
12394     checkTab : function(e){
12395         if(e.getKey() == e.TAB){
12396             this.triggerBlur();
12397         }
12398     },
12399
12400     // private
12401     onBlur : function(){
12402         // do nothing
12403     },
12404
12405     // private
12406     mimicBlur : function(e, t){
12407         /*
12408         if(!this.wrap.contains(t) && this.validateBlur()){
12409             this.triggerBlur();
12410         }
12411         */
12412     },
12413
12414     // private
12415     triggerBlur : function(){
12416         this.mimicing = false;
12417         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12418         if(this.monitorTab){
12419             this.el.un("keydown", this.checkTab, this);
12420         }
12421         //this.wrap.removeClass('x-trigger-wrap-focus');
12422         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12423     },
12424
12425     // private
12426     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12427     validateBlur : function(e, t){
12428         return true;
12429     },
12430
12431     // private
12432     onDisable : function(){
12433         this.inputEl().dom.disabled = true;
12434         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12435         //if(this.wrap){
12436         //    this.wrap.addClass('x-item-disabled');
12437         //}
12438     },
12439
12440     // private
12441     onEnable : function(){
12442         this.inputEl().dom.disabled = false;
12443         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12444         //if(this.wrap){
12445         //    this.el.removeClass('x-item-disabled');
12446         //}
12447     },
12448
12449     // private
12450     onShow : function(){
12451         var ae = this.getActionEl();
12452         
12453         if(ae){
12454             ae.dom.style.display = '';
12455             ae.dom.style.visibility = 'visible';
12456         }
12457     },
12458
12459     // private
12460     
12461     onHide : function(){
12462         var ae = this.getActionEl();
12463         ae.dom.style.display = 'none';
12464     },
12465
12466     /**
12467      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12468      * by an implementing function.
12469      * @method
12470      * @param {EventObject} e
12471      */
12472     onTriggerClick : Roo.emptyFn
12473 });
12474  
12475 /*
12476 * Licence: LGPL
12477 */
12478
12479 /**
12480  * @class Roo.bootstrap.CardUploader
12481  * @extends Roo.bootstrap.Button
12482  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12483  * @cfg {Number} errorTimeout default 3000
12484  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12485  * @cfg {Array}  html The button text.
12486
12487  *
12488  * @constructor
12489  * Create a new CardUploader
12490  * @param {Object} config The config object
12491  */
12492
12493 Roo.bootstrap.CardUploader = function(config){
12494     
12495  
12496     
12497     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12498     
12499     
12500     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12501         return r.data.id
12502      });
12503     
12504      this.addEvents({
12505          // raw events
12506         /**
12507          * @event view
12508          * When a image is clicked on - and needs to display a slideshow or similar..
12509          * @param {Roo.bootstrap.Card} this
12510          * @param {Roo.bootstrap.Card} The card containing the image data (.data is a property with image info.)
12511          *
12512          */
12513         'view' : true,
12514          /**
12515          * @event download
12516          * When a the download link is clicked
12517          * @param {Roo.bootstrap.Card} this
12518          * @param {Roo.bootstrap.Card} The card containing the image data (.data is a property with image info.)
12519          */
12520         'download' : true
12521         
12522     });
12523 };
12524 };
12525
12526 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12527     
12528      
12529     errorTimeout : 3000,
12530      
12531     images : false,
12532    
12533     fileCollection : false,
12534     allowBlank : true,
12535     
12536     getAutoCreate : function()
12537     {
12538         
12539         var cfg =  {
12540             cls :'form-group' ,
12541             cn : [
12542                
12543                 {
12544                     tag: 'label',
12545                    //cls : 'input-group-addon',
12546                     html : this.fieldLabel
12547
12548                 },
12549
12550                 {
12551                     tag: 'input',
12552                     type : 'hidden',
12553                     name : this.name,
12554                     value : this.value,
12555                     cls : 'd-none  form-control'
12556                 },
12557                 
12558                 {
12559                     tag: 'input',
12560                     multiple : 'multiple',
12561                     type : 'file',
12562                     cls : 'd-none  roo-card-upload-selector'
12563                 },
12564                 
12565                 {
12566                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12567                 },
12568                 {
12569                     cls : 'card-columns roo-card-uploader-container'
12570                 }
12571
12572             ]
12573         };
12574            
12575          
12576         return cfg;
12577     },
12578     
12579     getChildContainer : function() /// what children are added to.
12580     {
12581         return this.containerEl;
12582     },
12583    
12584     getButtonContainer : function() /// what children are added to.
12585     {
12586         return this.el.select(".roo-card-uploader-button-container").first();
12587     },
12588    
12589     initEvents : function()
12590     {
12591         
12592         Roo.bootstrap.Input.prototype.initEvents.call(this);
12593         
12594         var t = this;
12595         this.addxtype({
12596             xns: Roo.bootstrap,
12597
12598             xtype : 'Button',
12599             container_method : 'getButtonContainer' ,            
12600             html :  this.html, // fix changable?
12601             cls : 'w-100 ',
12602             listeners : {
12603                 'click' : function(btn, e) {
12604                     t.onClick(e);
12605                 }
12606             }
12607         });
12608         
12609         
12610         
12611         
12612         this.urlAPI = (window.createObjectURL && window) || 
12613                                 (window.URL && URL.revokeObjectURL && URL) || 
12614                                 (window.webkitURL && webkitURL);
12615                         
12616          
12617          
12618          
12619         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12620         
12621         this.selectorEl.on('change', this.onFileSelected, this);
12622         if (this.images) {
12623             var t = this;
12624             this.images.forEach(function(img) {
12625                 t.addCard(img)
12626             });
12627             this.images = false;
12628         }
12629         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12630          
12631        
12632     },
12633     
12634    
12635     onClick : function(e)
12636     {
12637         e.preventDefault();
12638          
12639         this.selectorEl.dom.click();
12640          
12641     },
12642     
12643     onFileSelected : function(e)
12644     {
12645         e.preventDefault();
12646         
12647         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12648             return;
12649         }
12650         
12651         Roo.each(this.selectorEl.dom.files, function(file){    
12652             this.addFile(file);
12653         }, this);
12654          
12655     },
12656     
12657       
12658     
12659       
12660     
12661     addFile : function(file)
12662     {
12663            
12664         if(typeof(file) === 'string'){
12665             throw "Add file by name?"; // should not happen
12666             return;
12667         }
12668         
12669         if(!file || !this.urlAPI){
12670             return;
12671         }
12672         
12673         // file;
12674         // file.type;
12675         
12676         var _this = this;
12677         
12678         
12679         var url = _this.urlAPI.createObjectURL( file);
12680            
12681         this.addCard({
12682             id : Roo.bootstrap.CardUploader.ID--,
12683             is_uploaded : false,
12684             src : url,
12685             srcfile : file,
12686             title : file.name,
12687             mimetype : file.type,
12688             preview : false,
12689             is_deleted : 0
12690         });
12691         
12692     },
12693     
12694     addCard : function (data)
12695     {
12696         // hidden input element?
12697         // if the file is not an image...
12698         //then we need to use something other that and header_image
12699         var t = this;
12700         //   remove.....
12701         var footer = [
12702             {
12703                 xns : Roo.bootstrap,
12704                 xtype : 'CardFooter',
12705                  items: [
12706                     {
12707                         xns : Roo.bootstrap,
12708                         xtype : 'Element',
12709                         cls : 'd-flex',
12710                         items : [
12711                             
12712                             {
12713                                 xns : Roo.bootstrap,
12714                                 xtype : 'Button',
12715                                 html : String.format("<small>{0}</small>", data.title),
12716                                 cls : 'col-10 text-left',
12717                                 size: 'sm',
12718                                 weight: 'link',
12719                                 fa : 'download',
12720                                 listeners : {
12721                                     click : function() {
12722                                      
12723                                         t.fireEvent( "download", t, data );
12724                                     }
12725                                 }
12726                             },
12727                           
12728                             {
12729                                 xns : Roo.bootstrap,
12730                                 xtype : 'Button',
12731                                 style: 'max-height: 28px; ',
12732                                 size : 'sm',
12733                                 weight: 'danger',
12734                                 cls : 'col-2',
12735                                 fa : 'times',
12736                                 listeners : {
12737                                     click : function() {
12738                                         t.removeCard(data.id)
12739                                     }
12740                                 }
12741                             }
12742                         ]
12743                     }
12744                     
12745                 ] 
12746             }
12747             
12748         ];
12749         
12750         var cn = this.addxtype(
12751             {
12752                  
12753                 xns : Roo.bootstrap,
12754                 xtype : 'Card',
12755                 closeable : true,
12756                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12757                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12758                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12759                 data : data,
12760                 html : false,
12761                  
12762                 items : footer,
12763                 initEvents : function() {
12764                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12765                     var card = this;
12766                     this.imgEl = this.el.select('.card-img-top').first();
12767                     if (this.imgEl) {
12768                         this.imgEl.on('click', function() { t.fireEvent( "preview", t, data ); }, this);
12769                         this.imgEl.set({ 'pointer' : 'cursor' });
12770                                   
12771                     }
12772                     this.getCardFooter().addClass('p-1');
12773                     
12774                   
12775                 }
12776                 
12777             }
12778         );
12779         // dont' really need ot update items.
12780         // this.items.push(cn);
12781         this.fileCollection.add(cn);
12782         
12783         if (!data.srcfile) {
12784             this.updateInput();
12785             return;
12786         }
12787             
12788         var _t = this;
12789         var reader = new FileReader();
12790         reader.addEventListener("load", function() {  
12791             data.srcdata =  reader.result;
12792             _t.updateInput();
12793         });
12794         reader.readAsDataURL(data.srcfile);
12795         
12796         
12797         
12798     },
12799     removeCard : function(id)
12800     {
12801         
12802         var card  = this.fileCollection.get(id);
12803         card.data.is_deleted = 1;
12804         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12805         //this.fileCollection.remove(card);
12806         //this.items = this.items.filter(function(e) { return e != card });
12807         // dont' really need ot update items.
12808         card.el.dom.parentNode.removeChild(card.el.dom);
12809         this.updateInput();
12810
12811         
12812     },
12813     reset: function()
12814     {
12815         this.fileCollection.each(function(card) {
12816             if (card.el.dom && card.el.dom.parentNode) {
12817                 card.el.dom.parentNode.removeChild(card.el.dom);
12818             }
12819         });
12820         this.fileCollection.clear();
12821         this.updateInput();
12822     },
12823     
12824     updateInput : function()
12825     {
12826          var data = [];
12827         this.fileCollection.each(function(e) {
12828             data.push(e.data);
12829             
12830         });
12831         this.inputEl().dom.value = JSON.stringify(data);
12832         
12833         
12834         
12835     }
12836     
12837     
12838 });
12839
12840
12841 Roo.bootstrap.CardUploader.ID = -1;/*
12842  * Based on:
12843  * Ext JS Library 1.1.1
12844  * Copyright(c) 2006-2007, Ext JS, LLC.
12845  *
12846  * Originally Released Under LGPL - original licence link has changed is not relivant.
12847  *
12848  * Fork - LGPL
12849  * <script type="text/javascript">
12850  */
12851
12852
12853 /**
12854  * @class Roo.data.SortTypes
12855  * @singleton
12856  * Defines the default sorting (casting?) comparison functions used when sorting data.
12857  */
12858 Roo.data.SortTypes = {
12859     /**
12860      * Default sort that does nothing
12861      * @param {Mixed} s The value being converted
12862      * @return {Mixed} The comparison value
12863      */
12864     none : function(s){
12865         return s;
12866     },
12867     
12868     /**
12869      * The regular expression used to strip tags
12870      * @type {RegExp}
12871      * @property
12872      */
12873     stripTagsRE : /<\/?[^>]+>/gi,
12874     
12875     /**
12876      * Strips all HTML tags to sort on text only
12877      * @param {Mixed} s The value being converted
12878      * @return {String} The comparison value
12879      */
12880     asText : function(s){
12881         return String(s).replace(this.stripTagsRE, "");
12882     },
12883     
12884     /**
12885      * Strips all HTML tags to sort on text only - Case insensitive
12886      * @param {Mixed} s The value being converted
12887      * @return {String} The comparison value
12888      */
12889     asUCText : function(s){
12890         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12891     },
12892     
12893     /**
12894      * Case insensitive string
12895      * @param {Mixed} s The value being converted
12896      * @return {String} The comparison value
12897      */
12898     asUCString : function(s) {
12899         return String(s).toUpperCase();
12900     },
12901     
12902     /**
12903      * Date sorting
12904      * @param {Mixed} s The value being converted
12905      * @return {Number} The comparison value
12906      */
12907     asDate : function(s) {
12908         if(!s){
12909             return 0;
12910         }
12911         if(s instanceof Date){
12912             return s.getTime();
12913         }
12914         return Date.parse(String(s));
12915     },
12916     
12917     /**
12918      * Float sorting
12919      * @param {Mixed} s The value being converted
12920      * @return {Float} The comparison value
12921      */
12922     asFloat : function(s) {
12923         var val = parseFloat(String(s).replace(/,/g, ""));
12924         if(isNaN(val)) {
12925             val = 0;
12926         }
12927         return val;
12928     },
12929     
12930     /**
12931      * Integer sorting
12932      * @param {Mixed} s The value being converted
12933      * @return {Number} The comparison value
12934      */
12935     asInt : function(s) {
12936         var val = parseInt(String(s).replace(/,/g, ""));
12937         if(isNaN(val)) {
12938             val = 0;
12939         }
12940         return val;
12941     }
12942 };/*
12943  * Based on:
12944  * Ext JS Library 1.1.1
12945  * Copyright(c) 2006-2007, Ext JS, LLC.
12946  *
12947  * Originally Released Under LGPL - original licence link has changed is not relivant.
12948  *
12949  * Fork - LGPL
12950  * <script type="text/javascript">
12951  */
12952
12953 /**
12954 * @class Roo.data.Record
12955  * Instances of this class encapsulate both record <em>definition</em> information, and record
12956  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12957  * to access Records cached in an {@link Roo.data.Store} object.<br>
12958  * <p>
12959  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12960  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12961  * objects.<br>
12962  * <p>
12963  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12964  * @constructor
12965  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12966  * {@link #create}. The parameters are the same.
12967  * @param {Array} data An associative Array of data values keyed by the field name.
12968  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12969  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12970  * not specified an integer id is generated.
12971  */
12972 Roo.data.Record = function(data, id){
12973     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12974     this.data = data;
12975 };
12976
12977 /**
12978  * Generate a constructor for a specific record layout.
12979  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12980  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12981  * Each field definition object may contain the following properties: <ul>
12982  * <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,
12983  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12984  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12985  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12986  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12987  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12988  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12989  * this may be omitted.</p></li>
12990  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12991  * <ul><li>auto (Default, implies no conversion)</li>
12992  * <li>string</li>
12993  * <li>int</li>
12994  * <li>float</li>
12995  * <li>boolean</li>
12996  * <li>date</li></ul></p></li>
12997  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12998  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12999  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
13000  * by the Reader into an object that will be stored in the Record. It is passed the
13001  * following parameters:<ul>
13002  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
13003  * </ul></p></li>
13004  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
13005  * </ul>
13006  * <br>usage:<br><pre><code>
13007 var TopicRecord = Roo.data.Record.create(
13008     {name: 'title', mapping: 'topic_title'},
13009     {name: 'author', mapping: 'username'},
13010     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
13011     {name: 'lastPost', mapping: 'post_time', type: 'date'},
13012     {name: 'lastPoster', mapping: 'user2'},
13013     {name: 'excerpt', mapping: 'post_text'}
13014 );
13015
13016 var myNewRecord = new TopicRecord({
13017     title: 'Do my job please',
13018     author: 'noobie',
13019     totalPosts: 1,
13020     lastPost: new Date(),
13021     lastPoster: 'Animal',
13022     excerpt: 'No way dude!'
13023 });
13024 myStore.add(myNewRecord);
13025 </code></pre>
13026  * @method create
13027  * @static
13028  */
13029 Roo.data.Record.create = function(o){
13030     var f = function(){
13031         f.superclass.constructor.apply(this, arguments);
13032     };
13033     Roo.extend(f, Roo.data.Record);
13034     var p = f.prototype;
13035     p.fields = new Roo.util.MixedCollection(false, function(field){
13036         return field.name;
13037     });
13038     for(var i = 0, len = o.length; i < len; i++){
13039         p.fields.add(new Roo.data.Field(o[i]));
13040     }
13041     f.getField = function(name){
13042         return p.fields.get(name);  
13043     };
13044     return f;
13045 };
13046
13047 Roo.data.Record.AUTO_ID = 1000;
13048 Roo.data.Record.EDIT = 'edit';
13049 Roo.data.Record.REJECT = 'reject';
13050 Roo.data.Record.COMMIT = 'commit';
13051
13052 Roo.data.Record.prototype = {
13053     /**
13054      * Readonly flag - true if this record has been modified.
13055      * @type Boolean
13056      */
13057     dirty : false,
13058     editing : false,
13059     error: null,
13060     modified: null,
13061
13062     // private
13063     join : function(store){
13064         this.store = store;
13065     },
13066
13067     /**
13068      * Set the named field to the specified value.
13069      * @param {String} name The name of the field to set.
13070      * @param {Object} value The value to set the field to.
13071      */
13072     set : function(name, value){
13073         if(this.data[name] == value){
13074             return;
13075         }
13076         this.dirty = true;
13077         if(!this.modified){
13078             this.modified = {};
13079         }
13080         if(typeof this.modified[name] == 'undefined'){
13081             this.modified[name] = this.data[name];
13082         }
13083         this.data[name] = value;
13084         if(!this.editing && this.store){
13085             this.store.afterEdit(this);
13086         }       
13087     },
13088
13089     /**
13090      * Get the value of the named field.
13091      * @param {String} name The name of the field to get the value of.
13092      * @return {Object} The value of the field.
13093      */
13094     get : function(name){
13095         return this.data[name]; 
13096     },
13097
13098     // private
13099     beginEdit : function(){
13100         this.editing = true;
13101         this.modified = {}; 
13102     },
13103
13104     // private
13105     cancelEdit : function(){
13106         this.editing = false;
13107         delete this.modified;
13108     },
13109
13110     // private
13111     endEdit : function(){
13112         this.editing = false;
13113         if(this.dirty && this.store){
13114             this.store.afterEdit(this);
13115         }
13116     },
13117
13118     /**
13119      * Usually called by the {@link Roo.data.Store} which owns the Record.
13120      * Rejects all changes made to the Record since either creation, or the last commit operation.
13121      * Modified fields are reverted to their original values.
13122      * <p>
13123      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13124      * of reject operations.
13125      */
13126     reject : function(){
13127         var m = this.modified;
13128         for(var n in m){
13129             if(typeof m[n] != "function"){
13130                 this.data[n] = m[n];
13131             }
13132         }
13133         this.dirty = false;
13134         delete this.modified;
13135         this.editing = false;
13136         if(this.store){
13137             this.store.afterReject(this);
13138         }
13139     },
13140
13141     /**
13142      * Usually called by the {@link Roo.data.Store} which owns the Record.
13143      * Commits all changes made to the Record since either creation, or the last commit operation.
13144      * <p>
13145      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13146      * of commit operations.
13147      */
13148     commit : function(){
13149         this.dirty = false;
13150         delete this.modified;
13151         this.editing = false;
13152         if(this.store){
13153             this.store.afterCommit(this);
13154         }
13155     },
13156
13157     // private
13158     hasError : function(){
13159         return this.error != null;
13160     },
13161
13162     // private
13163     clearError : function(){
13164         this.error = null;
13165     },
13166
13167     /**
13168      * Creates a copy of this record.
13169      * @param {String} id (optional) A new record id if you don't want to use this record's id
13170      * @return {Record}
13171      */
13172     copy : function(newId) {
13173         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13174     }
13175 };/*
13176  * Based on:
13177  * Ext JS Library 1.1.1
13178  * Copyright(c) 2006-2007, Ext JS, LLC.
13179  *
13180  * Originally Released Under LGPL - original licence link has changed is not relivant.
13181  *
13182  * Fork - LGPL
13183  * <script type="text/javascript">
13184  */
13185
13186
13187
13188 /**
13189  * @class Roo.data.Store
13190  * @extends Roo.util.Observable
13191  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13192  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13193  * <p>
13194  * 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
13195  * has no knowledge of the format of the data returned by the Proxy.<br>
13196  * <p>
13197  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13198  * instances from the data object. These records are cached and made available through accessor functions.
13199  * @constructor
13200  * Creates a new Store.
13201  * @param {Object} config A config object containing the objects needed for the Store to access data,
13202  * and read the data into Records.
13203  */
13204 Roo.data.Store = function(config){
13205     this.data = new Roo.util.MixedCollection(false);
13206     this.data.getKey = function(o){
13207         return o.id;
13208     };
13209     this.baseParams = {};
13210     // private
13211     this.paramNames = {
13212         "start" : "start",
13213         "limit" : "limit",
13214         "sort" : "sort",
13215         "dir" : "dir",
13216         "multisort" : "_multisort"
13217     };
13218
13219     if(config && config.data){
13220         this.inlineData = config.data;
13221         delete config.data;
13222     }
13223
13224     Roo.apply(this, config);
13225     
13226     if(this.reader){ // reader passed
13227         this.reader = Roo.factory(this.reader, Roo.data);
13228         this.reader.xmodule = this.xmodule || false;
13229         if(!this.recordType){
13230             this.recordType = this.reader.recordType;
13231         }
13232         if(this.reader.onMetaChange){
13233             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13234         }
13235     }
13236
13237     if(this.recordType){
13238         this.fields = this.recordType.prototype.fields;
13239     }
13240     this.modified = [];
13241
13242     this.addEvents({
13243         /**
13244          * @event datachanged
13245          * Fires when the data cache has changed, and a widget which is using this Store
13246          * as a Record cache should refresh its view.
13247          * @param {Store} this
13248          */
13249         datachanged : true,
13250         /**
13251          * @event metachange
13252          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13253          * @param {Store} this
13254          * @param {Object} meta The JSON metadata
13255          */
13256         metachange : true,
13257         /**
13258          * @event add
13259          * Fires when Records have been added to the Store
13260          * @param {Store} this
13261          * @param {Roo.data.Record[]} records The array of Records added
13262          * @param {Number} index The index at which the record(s) were added
13263          */
13264         add : true,
13265         /**
13266          * @event remove
13267          * Fires when a Record has been removed from the Store
13268          * @param {Store} this
13269          * @param {Roo.data.Record} record The Record that was removed
13270          * @param {Number} index The index at which the record was removed
13271          */
13272         remove : true,
13273         /**
13274          * @event update
13275          * Fires when a Record has been updated
13276          * @param {Store} this
13277          * @param {Roo.data.Record} record The Record that was updated
13278          * @param {String} operation The update operation being performed.  Value may be one of:
13279          * <pre><code>
13280  Roo.data.Record.EDIT
13281  Roo.data.Record.REJECT
13282  Roo.data.Record.COMMIT
13283          * </code></pre>
13284          */
13285         update : true,
13286         /**
13287          * @event clear
13288          * Fires when the data cache has been cleared.
13289          * @param {Store} this
13290          */
13291         clear : true,
13292         /**
13293          * @event beforeload
13294          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13295          * the load action will be canceled.
13296          * @param {Store} this
13297          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13298          */
13299         beforeload : true,
13300         /**
13301          * @event beforeloadadd
13302          * Fires after a new set of Records has been loaded.
13303          * @param {Store} this
13304          * @param {Roo.data.Record[]} records The Records that were loaded
13305          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13306          */
13307         beforeloadadd : true,
13308         /**
13309          * @event load
13310          * Fires after a new set of Records has been loaded, before they are added to the store.
13311          * @param {Store} this
13312          * @param {Roo.data.Record[]} records The Records that were loaded
13313          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13314          * @params {Object} return from reader
13315          */
13316         load : true,
13317         /**
13318          * @event loadexception
13319          * Fires if an exception occurs in the Proxy during loading.
13320          * Called with the signature of the Proxy's "loadexception" event.
13321          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13322          * 
13323          * @param {Proxy} 
13324          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13325          * @param {Object} load options 
13326          * @param {Object} jsonData from your request (normally this contains the Exception)
13327          */
13328         loadexception : true
13329     });
13330     
13331     if(this.proxy){
13332         this.proxy = Roo.factory(this.proxy, Roo.data);
13333         this.proxy.xmodule = this.xmodule || false;
13334         this.relayEvents(this.proxy,  ["loadexception"]);
13335     }
13336     this.sortToggle = {};
13337     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13338
13339     Roo.data.Store.superclass.constructor.call(this);
13340
13341     if(this.inlineData){
13342         this.loadData(this.inlineData);
13343         delete this.inlineData;
13344     }
13345 };
13346
13347 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13348      /**
13349     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13350     * without a remote query - used by combo/forms at present.
13351     */
13352     
13353     /**
13354     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13355     */
13356     /**
13357     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13358     */
13359     /**
13360     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13361     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13362     */
13363     /**
13364     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13365     * on any HTTP request
13366     */
13367     /**
13368     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13369     */
13370     /**
13371     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13372     */
13373     multiSort: false,
13374     /**
13375     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13376     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13377     */
13378     remoteSort : false,
13379
13380     /**
13381     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13382      * loaded or when a record is removed. (defaults to false).
13383     */
13384     pruneModifiedRecords : false,
13385
13386     // private
13387     lastOptions : null,
13388
13389     /**
13390      * Add Records to the Store and fires the add event.
13391      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13392      */
13393     add : function(records){
13394         records = [].concat(records);
13395         for(var i = 0, len = records.length; i < len; i++){
13396             records[i].join(this);
13397         }
13398         var index = this.data.length;
13399         this.data.addAll(records);
13400         this.fireEvent("add", this, records, index);
13401     },
13402
13403     /**
13404      * Remove a Record from the Store and fires the remove event.
13405      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13406      */
13407     remove : function(record){
13408         var index = this.data.indexOf(record);
13409         this.data.removeAt(index);
13410  
13411         if(this.pruneModifiedRecords){
13412             this.modified.remove(record);
13413         }
13414         this.fireEvent("remove", this, record, index);
13415     },
13416
13417     /**
13418      * Remove all Records from the Store and fires the clear event.
13419      */
13420     removeAll : function(){
13421         this.data.clear();
13422         if(this.pruneModifiedRecords){
13423             this.modified = [];
13424         }
13425         this.fireEvent("clear", this);
13426     },
13427
13428     /**
13429      * Inserts Records to the Store at the given index and fires the add event.
13430      * @param {Number} index The start index at which to insert the passed Records.
13431      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13432      */
13433     insert : function(index, records){
13434         records = [].concat(records);
13435         for(var i = 0, len = records.length; i < len; i++){
13436             this.data.insert(index, records[i]);
13437             records[i].join(this);
13438         }
13439         this.fireEvent("add", this, records, index);
13440     },
13441
13442     /**
13443      * Get the index within the cache of the passed Record.
13444      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13445      * @return {Number} The index of the passed Record. Returns -1 if not found.
13446      */
13447     indexOf : function(record){
13448         return this.data.indexOf(record);
13449     },
13450
13451     /**
13452      * Get the index within the cache of the Record with the passed id.
13453      * @param {String} id The id of the Record to find.
13454      * @return {Number} The index of the Record. Returns -1 if not found.
13455      */
13456     indexOfId : function(id){
13457         return this.data.indexOfKey(id);
13458     },
13459
13460     /**
13461      * Get the Record with the specified id.
13462      * @param {String} id The id of the Record to find.
13463      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13464      */
13465     getById : function(id){
13466         return this.data.key(id);
13467     },
13468
13469     /**
13470      * Get the Record at the specified index.
13471      * @param {Number} index The index of the Record to find.
13472      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13473      */
13474     getAt : function(index){
13475         return this.data.itemAt(index);
13476     },
13477
13478     /**
13479      * Returns a range of Records between specified indices.
13480      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13481      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13482      * @return {Roo.data.Record[]} An array of Records
13483      */
13484     getRange : function(start, end){
13485         return this.data.getRange(start, end);
13486     },
13487
13488     // private
13489     storeOptions : function(o){
13490         o = Roo.apply({}, o);
13491         delete o.callback;
13492         delete o.scope;
13493         this.lastOptions = o;
13494     },
13495
13496     /**
13497      * Loads the Record cache from the configured Proxy using the configured Reader.
13498      * <p>
13499      * If using remote paging, then the first load call must specify the <em>start</em>
13500      * and <em>limit</em> properties in the options.params property to establish the initial
13501      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13502      * <p>
13503      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13504      * and this call will return before the new data has been loaded. Perform any post-processing
13505      * in a callback function, or in a "load" event handler.</strong>
13506      * <p>
13507      * @param {Object} options An object containing properties which control loading options:<ul>
13508      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13509      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13510      * passed the following arguments:<ul>
13511      * <li>r : Roo.data.Record[]</li>
13512      * <li>options: Options object from the load call</li>
13513      * <li>success: Boolean success indicator</li></ul></li>
13514      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13515      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13516      * </ul>
13517      */
13518     load : function(options){
13519         options = options || {};
13520         if(this.fireEvent("beforeload", this, options) !== false){
13521             this.storeOptions(options);
13522             var p = Roo.apply(options.params || {}, this.baseParams);
13523             // if meta was not loaded from remote source.. try requesting it.
13524             if (!this.reader.metaFromRemote) {
13525                 p._requestMeta = 1;
13526             }
13527             if(this.sortInfo && this.remoteSort){
13528                 var pn = this.paramNames;
13529                 p[pn["sort"]] = this.sortInfo.field;
13530                 p[pn["dir"]] = this.sortInfo.direction;
13531             }
13532             if (this.multiSort) {
13533                 var pn = this.paramNames;
13534                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13535             }
13536             
13537             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13538         }
13539     },
13540
13541     /**
13542      * Reloads the Record cache from the configured Proxy using the configured Reader and
13543      * the options from the last load operation performed.
13544      * @param {Object} options (optional) An object containing properties which may override the options
13545      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13546      * the most recently used options are reused).
13547      */
13548     reload : function(options){
13549         this.load(Roo.applyIf(options||{}, this.lastOptions));
13550     },
13551
13552     // private
13553     // Called as a callback by the Reader during a load operation.
13554     loadRecords : function(o, options, success){
13555         if(!o || success === false){
13556             if(success !== false){
13557                 this.fireEvent("load", this, [], options, o);
13558             }
13559             if(options.callback){
13560                 options.callback.call(options.scope || this, [], options, false);
13561             }
13562             return;
13563         }
13564         // if data returned failure - throw an exception.
13565         if (o.success === false) {
13566             // show a message if no listener is registered.
13567             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13568                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13569             }
13570             // loadmask wil be hooked into this..
13571             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13572             return;
13573         }
13574         var r = o.records, t = o.totalRecords || r.length;
13575         
13576         this.fireEvent("beforeloadadd", this, r, options, o);
13577         
13578         if(!options || options.add !== true){
13579             if(this.pruneModifiedRecords){
13580                 this.modified = [];
13581             }
13582             for(var i = 0, len = r.length; i < len; i++){
13583                 r[i].join(this);
13584             }
13585             if(this.snapshot){
13586                 this.data = this.snapshot;
13587                 delete this.snapshot;
13588             }
13589             this.data.clear();
13590             this.data.addAll(r);
13591             this.totalLength = t;
13592             this.applySort();
13593             this.fireEvent("datachanged", this);
13594         }else{
13595             this.totalLength = Math.max(t, this.data.length+r.length);
13596             this.add(r);
13597         }
13598         
13599         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13600                 
13601             var e = new Roo.data.Record({});
13602
13603             e.set(this.parent.displayField, this.parent.emptyTitle);
13604             e.set(this.parent.valueField, '');
13605
13606             this.insert(0, e);
13607         }
13608             
13609         this.fireEvent("load", this, r, options, o);
13610         if(options.callback){
13611             options.callback.call(options.scope || this, r, options, true);
13612         }
13613     },
13614
13615
13616     /**
13617      * Loads data from a passed data block. A Reader which understands the format of the data
13618      * must have been configured in the constructor.
13619      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13620      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13621      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13622      */
13623     loadData : function(o, append){
13624         var r = this.reader.readRecords(o);
13625         this.loadRecords(r, {add: append}, true);
13626     },
13627     
13628      /**
13629      * using 'cn' the nested child reader read the child array into it's child stores.
13630      * @param {Object} rec The record with a 'children array
13631      */
13632     loadDataFromChildren : function(rec)
13633     {
13634         this.loadData(this.reader.toLoadData(rec));
13635     },
13636     
13637
13638     /**
13639      * Gets the number of cached records.
13640      * <p>
13641      * <em>If using paging, this may not be the total size of the dataset. If the data object
13642      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13643      * the data set size</em>
13644      */
13645     getCount : function(){
13646         return this.data.length || 0;
13647     },
13648
13649     /**
13650      * Gets the total number of records in the dataset as returned by the server.
13651      * <p>
13652      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13653      * the dataset size</em>
13654      */
13655     getTotalCount : function(){
13656         return this.totalLength || 0;
13657     },
13658
13659     /**
13660      * Returns the sort state of the Store as an object with two properties:
13661      * <pre><code>
13662  field {String} The name of the field by which the Records are sorted
13663  direction {String} The sort order, "ASC" or "DESC"
13664      * </code></pre>
13665      */
13666     getSortState : function(){
13667         return this.sortInfo;
13668     },
13669
13670     // private
13671     applySort : function(){
13672         if(this.sortInfo && !this.remoteSort){
13673             var s = this.sortInfo, f = s.field;
13674             var st = this.fields.get(f).sortType;
13675             var fn = function(r1, r2){
13676                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13677                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13678             };
13679             this.data.sort(s.direction, fn);
13680             if(this.snapshot && this.snapshot != this.data){
13681                 this.snapshot.sort(s.direction, fn);
13682             }
13683         }
13684     },
13685
13686     /**
13687      * Sets the default sort column and order to be used by the next load operation.
13688      * @param {String} fieldName The name of the field to sort by.
13689      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13690      */
13691     setDefaultSort : function(field, dir){
13692         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13693     },
13694
13695     /**
13696      * Sort the Records.
13697      * If remote sorting is used, the sort is performed on the server, and the cache is
13698      * reloaded. If local sorting is used, the cache is sorted internally.
13699      * @param {String} fieldName The name of the field to sort by.
13700      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13701      */
13702     sort : function(fieldName, dir){
13703         var f = this.fields.get(fieldName);
13704         if(!dir){
13705             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13706             
13707             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13708                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13709             }else{
13710                 dir = f.sortDir;
13711             }
13712         }
13713         this.sortToggle[f.name] = dir;
13714         this.sortInfo = {field: f.name, direction: dir};
13715         if(!this.remoteSort){
13716             this.applySort();
13717             this.fireEvent("datachanged", this);
13718         }else{
13719             this.load(this.lastOptions);
13720         }
13721     },
13722
13723     /**
13724      * Calls the specified function for each of the Records in the cache.
13725      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13726      * Returning <em>false</em> aborts and exits the iteration.
13727      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13728      */
13729     each : function(fn, scope){
13730         this.data.each(fn, scope);
13731     },
13732
13733     /**
13734      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13735      * (e.g., during paging).
13736      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13737      */
13738     getModifiedRecords : function(){
13739         return this.modified;
13740     },
13741
13742     // private
13743     createFilterFn : function(property, value, anyMatch){
13744         if(!value.exec){ // not a regex
13745             value = String(value);
13746             if(value.length == 0){
13747                 return false;
13748             }
13749             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13750         }
13751         return function(r){
13752             return value.test(r.data[property]);
13753         };
13754     },
13755
13756     /**
13757      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13758      * @param {String} property A field on your records
13759      * @param {Number} start The record index to start at (defaults to 0)
13760      * @param {Number} end The last record index to include (defaults to length - 1)
13761      * @return {Number} The sum
13762      */
13763     sum : function(property, start, end){
13764         var rs = this.data.items, v = 0;
13765         start = start || 0;
13766         end = (end || end === 0) ? end : rs.length-1;
13767
13768         for(var i = start; i <= end; i++){
13769             v += (rs[i].data[property] || 0);
13770         }
13771         return v;
13772     },
13773
13774     /**
13775      * Filter the records by a specified property.
13776      * @param {String} field A field on your records
13777      * @param {String/RegExp} value Either a string that the field
13778      * should start with or a RegExp to test against the field
13779      * @param {Boolean} anyMatch True to match any part not just the beginning
13780      */
13781     filter : function(property, value, anyMatch){
13782         var fn = this.createFilterFn(property, value, anyMatch);
13783         return fn ? this.filterBy(fn) : this.clearFilter();
13784     },
13785
13786     /**
13787      * Filter by a function. The specified function will be called with each
13788      * record in this data source. If the function returns true the record is included,
13789      * otherwise it is filtered.
13790      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13791      * @param {Object} scope (optional) The scope of the function (defaults to this)
13792      */
13793     filterBy : function(fn, scope){
13794         this.snapshot = this.snapshot || this.data;
13795         this.data = this.queryBy(fn, scope||this);
13796         this.fireEvent("datachanged", this);
13797     },
13798
13799     /**
13800      * Query the records by a specified property.
13801      * @param {String} field A field on your records
13802      * @param {String/RegExp} value Either a string that the field
13803      * should start with or a RegExp to test against the field
13804      * @param {Boolean} anyMatch True to match any part not just the beginning
13805      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13806      */
13807     query : function(property, value, anyMatch){
13808         var fn = this.createFilterFn(property, value, anyMatch);
13809         return fn ? this.queryBy(fn) : this.data.clone();
13810     },
13811
13812     /**
13813      * Query by a function. The specified function will be called with each
13814      * record in this data source. If the function returns true the record is included
13815      * in the results.
13816      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13817      * @param {Object} scope (optional) The scope of the function (defaults to this)
13818       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13819      **/
13820     queryBy : function(fn, scope){
13821         var data = this.snapshot || this.data;
13822         return data.filterBy(fn, scope||this);
13823     },
13824
13825     /**
13826      * Collects unique values for a particular dataIndex from this store.
13827      * @param {String} dataIndex The property to collect
13828      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13829      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13830      * @return {Array} An array of the unique values
13831      **/
13832     collect : function(dataIndex, allowNull, bypassFilter){
13833         var d = (bypassFilter === true && this.snapshot) ?
13834                 this.snapshot.items : this.data.items;
13835         var v, sv, r = [], l = {};
13836         for(var i = 0, len = d.length; i < len; i++){
13837             v = d[i].data[dataIndex];
13838             sv = String(v);
13839             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13840                 l[sv] = true;
13841                 r[r.length] = v;
13842             }
13843         }
13844         return r;
13845     },
13846
13847     /**
13848      * Revert to a view of the Record cache with no filtering applied.
13849      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13850      */
13851     clearFilter : function(suppressEvent){
13852         if(this.snapshot && this.snapshot != this.data){
13853             this.data = this.snapshot;
13854             delete this.snapshot;
13855             if(suppressEvent !== true){
13856                 this.fireEvent("datachanged", this);
13857             }
13858         }
13859     },
13860
13861     // private
13862     afterEdit : function(record){
13863         if(this.modified.indexOf(record) == -1){
13864             this.modified.push(record);
13865         }
13866         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13867     },
13868     
13869     // private
13870     afterReject : function(record){
13871         this.modified.remove(record);
13872         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13873     },
13874
13875     // private
13876     afterCommit : function(record){
13877         this.modified.remove(record);
13878         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13879     },
13880
13881     /**
13882      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13883      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13884      */
13885     commitChanges : function(){
13886         var m = this.modified.slice(0);
13887         this.modified = [];
13888         for(var i = 0, len = m.length; i < len; i++){
13889             m[i].commit();
13890         }
13891     },
13892
13893     /**
13894      * Cancel outstanding changes on all changed records.
13895      */
13896     rejectChanges : function(){
13897         var m = this.modified.slice(0);
13898         this.modified = [];
13899         for(var i = 0, len = m.length; i < len; i++){
13900             m[i].reject();
13901         }
13902     },
13903
13904     onMetaChange : function(meta, rtype, o){
13905         this.recordType = rtype;
13906         this.fields = rtype.prototype.fields;
13907         delete this.snapshot;
13908         this.sortInfo = meta.sortInfo || this.sortInfo;
13909         this.modified = [];
13910         this.fireEvent('metachange', this, this.reader.meta);
13911     },
13912     
13913     moveIndex : function(data, type)
13914     {
13915         var index = this.indexOf(data);
13916         
13917         var newIndex = index + type;
13918         
13919         this.remove(data);
13920         
13921         this.insert(newIndex, data);
13922         
13923     }
13924 });/*
13925  * Based on:
13926  * Ext JS Library 1.1.1
13927  * Copyright(c) 2006-2007, Ext JS, LLC.
13928  *
13929  * Originally Released Under LGPL - original licence link has changed is not relivant.
13930  *
13931  * Fork - LGPL
13932  * <script type="text/javascript">
13933  */
13934
13935 /**
13936  * @class Roo.data.SimpleStore
13937  * @extends Roo.data.Store
13938  * Small helper class to make creating Stores from Array data easier.
13939  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13940  * @cfg {Array} fields An array of field definition objects, or field name strings.
13941  * @cfg {Object} an existing reader (eg. copied from another store)
13942  * @cfg {Array} data The multi-dimensional array of data
13943  * @constructor
13944  * @param {Object} config
13945  */
13946 Roo.data.SimpleStore = function(config)
13947 {
13948     Roo.data.SimpleStore.superclass.constructor.call(this, {
13949         isLocal : true,
13950         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13951                 id: config.id
13952             },
13953             Roo.data.Record.create(config.fields)
13954         ),
13955         proxy : new Roo.data.MemoryProxy(config.data)
13956     });
13957     this.load();
13958 };
13959 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13960  * Based on:
13961  * Ext JS Library 1.1.1
13962  * Copyright(c) 2006-2007, Ext JS, LLC.
13963  *
13964  * Originally Released Under LGPL - original licence link has changed is not relivant.
13965  *
13966  * Fork - LGPL
13967  * <script type="text/javascript">
13968  */
13969
13970 /**
13971 /**
13972  * @extends Roo.data.Store
13973  * @class Roo.data.JsonStore
13974  * Small helper class to make creating Stores for JSON data easier. <br/>
13975 <pre><code>
13976 var store = new Roo.data.JsonStore({
13977     url: 'get-images.php',
13978     root: 'images',
13979     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13980 });
13981 </code></pre>
13982  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13983  * JsonReader and HttpProxy (unless inline data is provided).</b>
13984  * @cfg {Array} fields An array of field definition objects, or field name strings.
13985  * @constructor
13986  * @param {Object} config
13987  */
13988 Roo.data.JsonStore = function(c){
13989     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13990         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13991         reader: new Roo.data.JsonReader(c, c.fields)
13992     }));
13993 };
13994 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13995  * Based on:
13996  * Ext JS Library 1.1.1
13997  * Copyright(c) 2006-2007, Ext JS, LLC.
13998  *
13999  * Originally Released Under LGPL - original licence link has changed is not relivant.
14000  *
14001  * Fork - LGPL
14002  * <script type="text/javascript">
14003  */
14004
14005  
14006 Roo.data.Field = function(config){
14007     if(typeof config == "string"){
14008         config = {name: config};
14009     }
14010     Roo.apply(this, config);
14011     
14012     if(!this.type){
14013         this.type = "auto";
14014     }
14015     
14016     var st = Roo.data.SortTypes;
14017     // named sortTypes are supported, here we look them up
14018     if(typeof this.sortType == "string"){
14019         this.sortType = st[this.sortType];
14020     }
14021     
14022     // set default sortType for strings and dates
14023     if(!this.sortType){
14024         switch(this.type){
14025             case "string":
14026                 this.sortType = st.asUCString;
14027                 break;
14028             case "date":
14029                 this.sortType = st.asDate;
14030                 break;
14031             default:
14032                 this.sortType = st.none;
14033         }
14034     }
14035
14036     // define once
14037     var stripRe = /[\$,%]/g;
14038
14039     // prebuilt conversion function for this field, instead of
14040     // switching every time we're reading a value
14041     if(!this.convert){
14042         var cv, dateFormat = this.dateFormat;
14043         switch(this.type){
14044             case "":
14045             case "auto":
14046             case undefined:
14047                 cv = function(v){ return v; };
14048                 break;
14049             case "string":
14050                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14051                 break;
14052             case "int":
14053                 cv = function(v){
14054                     return v !== undefined && v !== null && v !== '' ?
14055                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14056                     };
14057                 break;
14058             case "float":
14059                 cv = function(v){
14060                     return v !== undefined && v !== null && v !== '' ?
14061                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14062                     };
14063                 break;
14064             case "bool":
14065             case "boolean":
14066                 cv = function(v){ return v === true || v === "true" || v == 1; };
14067                 break;
14068             case "date":
14069                 cv = function(v){
14070                     if(!v){
14071                         return '';
14072                     }
14073                     if(v instanceof Date){
14074                         return v;
14075                     }
14076                     if(dateFormat){
14077                         if(dateFormat == "timestamp"){
14078                             return new Date(v*1000);
14079                         }
14080                         return Date.parseDate(v, dateFormat);
14081                     }
14082                     var parsed = Date.parse(v);
14083                     return parsed ? new Date(parsed) : null;
14084                 };
14085              break;
14086             
14087         }
14088         this.convert = cv;
14089     }
14090 };
14091
14092 Roo.data.Field.prototype = {
14093     dateFormat: null,
14094     defaultValue: "",
14095     mapping: null,
14096     sortType : null,
14097     sortDir : "ASC"
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108  
14109 // Base class for reading structured data from a data source.  This class is intended to be
14110 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14111
14112 /**
14113  * @class Roo.data.DataReader
14114  * Base class for reading structured data from a data source.  This class is intended to be
14115  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14116  */
14117
14118 Roo.data.DataReader = function(meta, recordType){
14119     
14120     this.meta = meta;
14121     
14122     this.recordType = recordType instanceof Array ? 
14123         Roo.data.Record.create(recordType) : recordType;
14124 };
14125
14126 Roo.data.DataReader.prototype = {
14127     
14128     
14129     readerType : 'Data',
14130      /**
14131      * Create an empty record
14132      * @param {Object} data (optional) - overlay some values
14133      * @return {Roo.data.Record} record created.
14134      */
14135     newRow :  function(d) {
14136         var da =  {};
14137         this.recordType.prototype.fields.each(function(c) {
14138             switch( c.type) {
14139                 case 'int' : da[c.name] = 0; break;
14140                 case 'date' : da[c.name] = new Date(); break;
14141                 case 'float' : da[c.name] = 0.0; break;
14142                 case 'boolean' : da[c.name] = false; break;
14143                 default : da[c.name] = ""; break;
14144             }
14145             
14146         });
14147         return new this.recordType(Roo.apply(da, d));
14148     }
14149     
14150     
14151 };/*
14152  * Based on:
14153  * Ext JS Library 1.1.1
14154  * Copyright(c) 2006-2007, Ext JS, LLC.
14155  *
14156  * Originally Released Under LGPL - original licence link has changed is not relivant.
14157  *
14158  * Fork - LGPL
14159  * <script type="text/javascript">
14160  */
14161
14162 /**
14163  * @class Roo.data.DataProxy
14164  * @extends Roo.data.Observable
14165  * This class is an abstract base class for implementations which provide retrieval of
14166  * unformatted data objects.<br>
14167  * <p>
14168  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14169  * (of the appropriate type which knows how to parse the data object) to provide a block of
14170  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14171  * <p>
14172  * Custom implementations must implement the load method as described in
14173  * {@link Roo.data.HttpProxy#load}.
14174  */
14175 Roo.data.DataProxy = function(){
14176     this.addEvents({
14177         /**
14178          * @event beforeload
14179          * Fires before a network request is made to retrieve a data object.
14180          * @param {Object} This DataProxy object.
14181          * @param {Object} params The params parameter to the load function.
14182          */
14183         beforeload : true,
14184         /**
14185          * @event load
14186          * Fires before the load method's callback is called.
14187          * @param {Object} This DataProxy object.
14188          * @param {Object} o The data object.
14189          * @param {Object} arg The callback argument object passed to the load function.
14190          */
14191         load : true,
14192         /**
14193          * @event loadexception
14194          * Fires if an Exception occurs during data retrieval.
14195          * @param {Object} This DataProxy object.
14196          * @param {Object} o The data object.
14197          * @param {Object} arg The callback argument object passed to the load function.
14198          * @param {Object} e The Exception.
14199          */
14200         loadexception : true
14201     });
14202     Roo.data.DataProxy.superclass.constructor.call(this);
14203 };
14204
14205 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14206
14207     /**
14208      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14209      */
14210 /*
14211  * Based on:
14212  * Ext JS Library 1.1.1
14213  * Copyright(c) 2006-2007, Ext JS, LLC.
14214  *
14215  * Originally Released Under LGPL - original licence link has changed is not relivant.
14216  *
14217  * Fork - LGPL
14218  * <script type="text/javascript">
14219  */
14220 /**
14221  * @class Roo.data.MemoryProxy
14222  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14223  * to the Reader when its load method is called.
14224  * @constructor
14225  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14226  */
14227 Roo.data.MemoryProxy = function(data){
14228     if (data.data) {
14229         data = data.data;
14230     }
14231     Roo.data.MemoryProxy.superclass.constructor.call(this);
14232     this.data = data;
14233 };
14234
14235 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14236     
14237     /**
14238      * Load data from the requested source (in this case an in-memory
14239      * data object passed to the constructor), read the data object into
14240      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14241      * process that block using the passed callback.
14242      * @param {Object} params This parameter is not used by the MemoryProxy class.
14243      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14244      * object into a block of Roo.data.Records.
14245      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14246      * The function must be passed <ul>
14247      * <li>The Record block object</li>
14248      * <li>The "arg" argument from the load function</li>
14249      * <li>A boolean success indicator</li>
14250      * </ul>
14251      * @param {Object} scope The scope in which to call the callback
14252      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14253      */
14254     load : function(params, reader, callback, scope, arg){
14255         params = params || {};
14256         var result;
14257         try {
14258             result = reader.readRecords(params.data ? params.data :this.data);
14259         }catch(e){
14260             this.fireEvent("loadexception", this, arg, null, e);
14261             callback.call(scope, null, arg, false);
14262             return;
14263         }
14264         callback.call(scope, result, arg, true);
14265     },
14266     
14267     // private
14268     update : function(params, records){
14269         
14270     }
14271 });/*
14272  * Based on:
14273  * Ext JS Library 1.1.1
14274  * Copyright(c) 2006-2007, Ext JS, LLC.
14275  *
14276  * Originally Released Under LGPL - original licence link has changed is not relivant.
14277  *
14278  * Fork - LGPL
14279  * <script type="text/javascript">
14280  */
14281 /**
14282  * @class Roo.data.HttpProxy
14283  * @extends Roo.data.DataProxy
14284  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14285  * configured to reference a certain URL.<br><br>
14286  * <p>
14287  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14288  * from which the running page was served.<br><br>
14289  * <p>
14290  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14291  * <p>
14292  * Be aware that to enable the browser to parse an XML document, the server must set
14293  * the Content-Type header in the HTTP response to "text/xml".
14294  * @constructor
14295  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14296  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14297  * will be used to make the request.
14298  */
14299 Roo.data.HttpProxy = function(conn){
14300     Roo.data.HttpProxy.superclass.constructor.call(this);
14301     // is conn a conn config or a real conn?
14302     this.conn = conn;
14303     this.useAjax = !conn || !conn.events;
14304   
14305 };
14306
14307 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14308     // thse are take from connection...
14309     
14310     /**
14311      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14312      */
14313     /**
14314      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14315      * extra parameters to each request made by this object. (defaults to undefined)
14316      */
14317     /**
14318      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14319      *  to each request made by this object. (defaults to undefined)
14320      */
14321     /**
14322      * @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)
14323      */
14324     /**
14325      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14326      */
14327      /**
14328      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14329      * @type Boolean
14330      */
14331   
14332
14333     /**
14334      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14335      * @type Boolean
14336      */
14337     /**
14338      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14339      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14340      * a finer-grained basis than the DataProxy events.
14341      */
14342     getConnection : function(){
14343         return this.useAjax ? Roo.Ajax : this.conn;
14344     },
14345
14346     /**
14347      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14348      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14349      * process that block using the passed callback.
14350      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14351      * for the request to the remote server.
14352      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14353      * object into a block of Roo.data.Records.
14354      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14355      * The function must be passed <ul>
14356      * <li>The Record block object</li>
14357      * <li>The "arg" argument from the load function</li>
14358      * <li>A boolean success indicator</li>
14359      * </ul>
14360      * @param {Object} scope The scope in which to call the callback
14361      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14362      */
14363     load : function(params, reader, callback, scope, arg){
14364         if(this.fireEvent("beforeload", this, params) !== false){
14365             var  o = {
14366                 params : params || {},
14367                 request: {
14368                     callback : callback,
14369                     scope : scope,
14370                     arg : arg
14371                 },
14372                 reader: reader,
14373                 callback : this.loadResponse,
14374                 scope: this
14375             };
14376             if(this.useAjax){
14377                 Roo.applyIf(o, this.conn);
14378                 if(this.activeRequest){
14379                     Roo.Ajax.abort(this.activeRequest);
14380                 }
14381                 this.activeRequest = Roo.Ajax.request(o);
14382             }else{
14383                 this.conn.request(o);
14384             }
14385         }else{
14386             callback.call(scope||this, null, arg, false);
14387         }
14388     },
14389
14390     // private
14391     loadResponse : function(o, success, response){
14392         delete this.activeRequest;
14393         if(!success){
14394             this.fireEvent("loadexception", this, o, response);
14395             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14396             return;
14397         }
14398         var result;
14399         try {
14400             result = o.reader.read(response);
14401         }catch(e){
14402             this.fireEvent("loadexception", this, o, response, e);
14403             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14404             return;
14405         }
14406         
14407         this.fireEvent("load", this, o, o.request.arg);
14408         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14409     },
14410
14411     // private
14412     update : function(dataSet){
14413
14414     },
14415
14416     // private
14417     updateResponse : function(dataSet){
14418
14419     }
14420 });/*
14421  * Based on:
14422  * Ext JS Library 1.1.1
14423  * Copyright(c) 2006-2007, Ext JS, LLC.
14424  *
14425  * Originally Released Under LGPL - original licence link has changed is not relivant.
14426  *
14427  * Fork - LGPL
14428  * <script type="text/javascript">
14429  */
14430
14431 /**
14432  * @class Roo.data.ScriptTagProxy
14433  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14434  * other than the originating domain of the running page.<br><br>
14435  * <p>
14436  * <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
14437  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14438  * <p>
14439  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14440  * source code that is used as the source inside a &lt;script> tag.<br><br>
14441  * <p>
14442  * In order for the browser to process the returned data, the server must wrap the data object
14443  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14444  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14445  * depending on whether the callback name was passed:
14446  * <p>
14447  * <pre><code>
14448 boolean scriptTag = false;
14449 String cb = request.getParameter("callback");
14450 if (cb != null) {
14451     scriptTag = true;
14452     response.setContentType("text/javascript");
14453 } else {
14454     response.setContentType("application/x-json");
14455 }
14456 Writer out = response.getWriter();
14457 if (scriptTag) {
14458     out.write(cb + "(");
14459 }
14460 out.print(dataBlock.toJsonString());
14461 if (scriptTag) {
14462     out.write(");");
14463 }
14464 </pre></code>
14465  *
14466  * @constructor
14467  * @param {Object} config A configuration object.
14468  */
14469 Roo.data.ScriptTagProxy = function(config){
14470     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14471     Roo.apply(this, config);
14472     this.head = document.getElementsByTagName("head")[0];
14473 };
14474
14475 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14476
14477 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14478     /**
14479      * @cfg {String} url The URL from which to request the data object.
14480      */
14481     /**
14482      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14483      */
14484     timeout : 30000,
14485     /**
14486      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14487      * the server the name of the callback function set up by the load call to process the returned data object.
14488      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14489      * javascript output which calls this named function passing the data object as its only parameter.
14490      */
14491     callbackParam : "callback",
14492     /**
14493      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14494      * name to the request.
14495      */
14496     nocache : true,
14497
14498     /**
14499      * Load data from the configured URL, read the data object into
14500      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14501      * process that block using the passed callback.
14502      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14503      * for the request to the remote server.
14504      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14505      * object into a block of Roo.data.Records.
14506      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14507      * The function must be passed <ul>
14508      * <li>The Record block object</li>
14509      * <li>The "arg" argument from the load function</li>
14510      * <li>A boolean success indicator</li>
14511      * </ul>
14512      * @param {Object} scope The scope in which to call the callback
14513      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14514      */
14515     load : function(params, reader, callback, scope, arg){
14516         if(this.fireEvent("beforeload", this, params) !== false){
14517
14518             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14519
14520             var url = this.url;
14521             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14522             if(this.nocache){
14523                 url += "&_dc=" + (new Date().getTime());
14524             }
14525             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14526             var trans = {
14527                 id : transId,
14528                 cb : "stcCallback"+transId,
14529                 scriptId : "stcScript"+transId,
14530                 params : params,
14531                 arg : arg,
14532                 url : url,
14533                 callback : callback,
14534                 scope : scope,
14535                 reader : reader
14536             };
14537             var conn = this;
14538
14539             window[trans.cb] = function(o){
14540                 conn.handleResponse(o, trans);
14541             };
14542
14543             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14544
14545             if(this.autoAbort !== false){
14546                 this.abort();
14547             }
14548
14549             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14550
14551             var script = document.createElement("script");
14552             script.setAttribute("src", url);
14553             script.setAttribute("type", "text/javascript");
14554             script.setAttribute("id", trans.scriptId);
14555             this.head.appendChild(script);
14556
14557             this.trans = trans;
14558         }else{
14559             callback.call(scope||this, null, arg, false);
14560         }
14561     },
14562
14563     // private
14564     isLoading : function(){
14565         return this.trans ? true : false;
14566     },
14567
14568     /**
14569      * Abort the current server request.
14570      */
14571     abort : function(){
14572         if(this.isLoading()){
14573             this.destroyTrans(this.trans);
14574         }
14575     },
14576
14577     // private
14578     destroyTrans : function(trans, isLoaded){
14579         this.head.removeChild(document.getElementById(trans.scriptId));
14580         clearTimeout(trans.timeoutId);
14581         if(isLoaded){
14582             window[trans.cb] = undefined;
14583             try{
14584                 delete window[trans.cb];
14585             }catch(e){}
14586         }else{
14587             // if hasn't been loaded, wait for load to remove it to prevent script error
14588             window[trans.cb] = function(){
14589                 window[trans.cb] = undefined;
14590                 try{
14591                     delete window[trans.cb];
14592                 }catch(e){}
14593             };
14594         }
14595     },
14596
14597     // private
14598     handleResponse : function(o, trans){
14599         this.trans = false;
14600         this.destroyTrans(trans, true);
14601         var result;
14602         try {
14603             result = trans.reader.readRecords(o);
14604         }catch(e){
14605             this.fireEvent("loadexception", this, o, trans.arg, e);
14606             trans.callback.call(trans.scope||window, null, trans.arg, false);
14607             return;
14608         }
14609         this.fireEvent("load", this, o, trans.arg);
14610         trans.callback.call(trans.scope||window, result, trans.arg, true);
14611     },
14612
14613     // private
14614     handleFailure : function(trans){
14615         this.trans = false;
14616         this.destroyTrans(trans, false);
14617         this.fireEvent("loadexception", this, null, trans.arg);
14618         trans.callback.call(trans.scope||window, null, trans.arg, false);
14619     }
14620 });/*
14621  * Based on:
14622  * Ext JS Library 1.1.1
14623  * Copyright(c) 2006-2007, Ext JS, LLC.
14624  *
14625  * Originally Released Under LGPL - original licence link has changed is not relivant.
14626  *
14627  * Fork - LGPL
14628  * <script type="text/javascript">
14629  */
14630
14631 /**
14632  * @class Roo.data.JsonReader
14633  * @extends Roo.data.DataReader
14634  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14635  * based on mappings in a provided Roo.data.Record constructor.
14636  * 
14637  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14638  * in the reply previously. 
14639  * 
14640  * <p>
14641  * Example code:
14642  * <pre><code>
14643 var RecordDef = Roo.data.Record.create([
14644     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14645     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14646 ]);
14647 var myReader = new Roo.data.JsonReader({
14648     totalProperty: "results",    // The property which contains the total dataset size (optional)
14649     root: "rows",                // The property which contains an Array of row objects
14650     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14651 }, RecordDef);
14652 </code></pre>
14653  * <p>
14654  * This would consume a JSON file like this:
14655  * <pre><code>
14656 { 'results': 2, 'rows': [
14657     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14658     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14659 }
14660 </code></pre>
14661  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14662  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14663  * paged from the remote server.
14664  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14665  * @cfg {String} root name of the property which contains the Array of row objects.
14666  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14667  * @cfg {Array} fields Array of field definition objects
14668  * @constructor
14669  * Create a new JsonReader
14670  * @param {Object} meta Metadata configuration options
14671  * @param {Object} recordType Either an Array of field definition objects,
14672  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14673  */
14674 Roo.data.JsonReader = function(meta, recordType){
14675     
14676     meta = meta || {};
14677     // set some defaults:
14678     Roo.applyIf(meta, {
14679         totalProperty: 'total',
14680         successProperty : 'success',
14681         root : 'data',
14682         id : 'id'
14683     });
14684     
14685     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14686 };
14687 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14688     
14689     readerType : 'Json',
14690     
14691     /**
14692      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14693      * Used by Store query builder to append _requestMeta to params.
14694      * 
14695      */
14696     metaFromRemote : false,
14697     /**
14698      * This method is only used by a DataProxy which has retrieved data from a remote server.
14699      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14700      * @return {Object} data A data block which is used by an Roo.data.Store object as
14701      * a cache of Roo.data.Records.
14702      */
14703     read : function(response){
14704         var json = response.responseText;
14705        
14706         var o = /* eval:var:o */ eval("("+json+")");
14707         if(!o) {
14708             throw {message: "JsonReader.read: Json object not found"};
14709         }
14710         
14711         if(o.metaData){
14712             
14713             delete this.ef;
14714             this.metaFromRemote = true;
14715             this.meta = o.metaData;
14716             this.recordType = Roo.data.Record.create(o.metaData.fields);
14717             this.onMetaChange(this.meta, this.recordType, o);
14718         }
14719         return this.readRecords(o);
14720     },
14721
14722     // private function a store will implement
14723     onMetaChange : function(meta, recordType, o){
14724
14725     },
14726
14727     /**
14728          * @ignore
14729          */
14730     simpleAccess: function(obj, subsc) {
14731         return obj[subsc];
14732     },
14733
14734         /**
14735          * @ignore
14736          */
14737     getJsonAccessor: function(){
14738         var re = /[\[\.]/;
14739         return function(expr) {
14740             try {
14741                 return(re.test(expr))
14742                     ? new Function("obj", "return obj." + expr)
14743                     : function(obj){
14744                         return obj[expr];
14745                     };
14746             } catch(e){}
14747             return Roo.emptyFn;
14748         };
14749     }(),
14750
14751     /**
14752      * Create a data block containing Roo.data.Records from an XML document.
14753      * @param {Object} o An object which contains an Array of row objects in the property specified
14754      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14755      * which contains the total size of the dataset.
14756      * @return {Object} data A data block which is used by an Roo.data.Store object as
14757      * a cache of Roo.data.Records.
14758      */
14759     readRecords : function(o){
14760         /**
14761          * After any data loads, the raw JSON data is available for further custom processing.
14762          * @type Object
14763          */
14764         this.o = o;
14765         var s = this.meta, Record = this.recordType,
14766             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14767
14768 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14769         if (!this.ef) {
14770             if(s.totalProperty) {
14771                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14772                 }
14773                 if(s.successProperty) {
14774                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14775                 }
14776                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14777                 if (s.id) {
14778                         var g = this.getJsonAccessor(s.id);
14779                         this.getId = function(rec) {
14780                                 var r = g(rec);  
14781                                 return (r === undefined || r === "") ? null : r;
14782                         };
14783                 } else {
14784                         this.getId = function(){return null;};
14785                 }
14786             this.ef = [];
14787             for(var jj = 0; jj < fl; jj++){
14788                 f = fi[jj];
14789                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14790                 this.ef[jj] = this.getJsonAccessor(map);
14791             }
14792         }
14793
14794         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14795         if(s.totalProperty){
14796             var vt = parseInt(this.getTotal(o), 10);
14797             if(!isNaN(vt)){
14798                 totalRecords = vt;
14799             }
14800         }
14801         if(s.successProperty){
14802             var vs = this.getSuccess(o);
14803             if(vs === false || vs === 'false'){
14804                 success = false;
14805             }
14806         }
14807         var records = [];
14808         for(var i = 0; i < c; i++){
14809                 var n = root[i];
14810             var values = {};
14811             var id = this.getId(n);
14812             for(var j = 0; j < fl; j++){
14813                 f = fi[j];
14814             var v = this.ef[j](n);
14815             if (!f.convert) {
14816                 Roo.log('missing convert for ' + f.name);
14817                 Roo.log(f);
14818                 continue;
14819             }
14820             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14821             }
14822             var record = new Record(values, id);
14823             record.json = n;
14824             records[i] = record;
14825         }
14826         return {
14827             raw : o,
14828             success : success,
14829             records : records,
14830             totalRecords : totalRecords
14831         };
14832     },
14833     // used when loading children.. @see loadDataFromChildren
14834     toLoadData: function(rec)
14835     {
14836         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14837         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14838         return { data : data, total : data.length };
14839         
14840     }
14841 });/*
14842  * Based on:
14843  * Ext JS Library 1.1.1
14844  * Copyright(c) 2006-2007, Ext JS, LLC.
14845  *
14846  * Originally Released Under LGPL - original licence link has changed is not relivant.
14847  *
14848  * Fork - LGPL
14849  * <script type="text/javascript">
14850  */
14851
14852 /**
14853  * @class Roo.data.ArrayReader
14854  * @extends Roo.data.DataReader
14855  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14856  * Each element of that Array represents a row of data fields. The
14857  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14858  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14859  * <p>
14860  * Example code:.
14861  * <pre><code>
14862 var RecordDef = Roo.data.Record.create([
14863     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14864     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14865 ]);
14866 var myReader = new Roo.data.ArrayReader({
14867     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14868 }, RecordDef);
14869 </code></pre>
14870  * <p>
14871  * This would consume an Array like this:
14872  * <pre><code>
14873 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14874   </code></pre>
14875  
14876  * @constructor
14877  * Create a new JsonReader
14878  * @param {Object} meta Metadata configuration options.
14879  * @param {Object|Array} recordType Either an Array of field definition objects
14880  * 
14881  * @cfg {Array} fields Array of field definition objects
14882  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14883  * as specified to {@link Roo.data.Record#create},
14884  * or an {@link Roo.data.Record} object
14885  *
14886  * 
14887  * created using {@link Roo.data.Record#create}.
14888  */
14889 Roo.data.ArrayReader = function(meta, recordType)
14890 {    
14891     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14892 };
14893
14894 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14895     
14896       /**
14897      * Create a data block containing Roo.data.Records from an XML document.
14898      * @param {Object} o An Array of row objects which represents the dataset.
14899      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14900      * a cache of Roo.data.Records.
14901      */
14902     readRecords : function(o)
14903     {
14904         var sid = this.meta ? this.meta.id : null;
14905         var recordType = this.recordType, fields = recordType.prototype.fields;
14906         var records = [];
14907         var root = o;
14908         for(var i = 0; i < root.length; i++){
14909                 var n = root[i];
14910             var values = {};
14911             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14912             for(var j = 0, jlen = fields.length; j < jlen; j++){
14913                 var f = fields.items[j];
14914                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14915                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14916                 v = f.convert(v);
14917                 values[f.name] = v;
14918             }
14919             var record = new recordType(values, id);
14920             record.json = n;
14921             records[records.length] = record;
14922         }
14923         return {
14924             records : records,
14925             totalRecords : records.length
14926         };
14927     },
14928     // used when loading children.. @see loadDataFromChildren
14929     toLoadData: function(rec)
14930     {
14931         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14932         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14933         
14934     }
14935     
14936     
14937 });/*
14938  * - LGPL
14939  * * 
14940  */
14941
14942 /**
14943  * @class Roo.bootstrap.ComboBox
14944  * @extends Roo.bootstrap.TriggerField
14945  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14946  * @cfg {Boolean} append (true|false) default false
14947  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14948  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14949  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14950  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14951  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14952  * @cfg {Boolean} animate default true
14953  * @cfg {Boolean} emptyResultText only for touch device
14954  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14955  * @cfg {String} emptyTitle default ''
14956  * @cfg {Number} width fixed with? experimental
14957  * @constructor
14958  * Create a new ComboBox.
14959  * @param {Object} config Configuration options
14960  */
14961 Roo.bootstrap.ComboBox = function(config){
14962     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14963     this.addEvents({
14964         /**
14965          * @event expand
14966          * Fires when the dropdown list is expanded
14967         * @param {Roo.bootstrap.ComboBox} combo This combo box
14968         */
14969         'expand' : true,
14970         /**
14971          * @event collapse
14972          * Fires when the dropdown list is collapsed
14973         * @param {Roo.bootstrap.ComboBox} combo This combo box
14974         */
14975         'collapse' : true,
14976         /**
14977          * @event beforeselect
14978          * Fires before a list item is selected. Return false to cancel the selection.
14979         * @param {Roo.bootstrap.ComboBox} combo This combo box
14980         * @param {Roo.data.Record} record The data record returned from the underlying store
14981         * @param {Number} index The index of the selected item in the dropdown list
14982         */
14983         'beforeselect' : true,
14984         /**
14985          * @event select
14986          * Fires when a list item is selected
14987         * @param {Roo.bootstrap.ComboBox} combo This combo box
14988         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14989         * @param {Number} index The index of the selected item in the dropdown list
14990         */
14991         'select' : true,
14992         /**
14993          * @event beforequery
14994          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14995          * The event object passed has these properties:
14996         * @param {Roo.bootstrap.ComboBox} combo This combo box
14997         * @param {String} query The query
14998         * @param {Boolean} forceAll true to force "all" query
14999         * @param {Boolean} cancel true to cancel the query
15000         * @param {Object} e The query event object
15001         */
15002         'beforequery': true,
15003          /**
15004          * @event add
15005          * Fires when the 'add' icon is pressed (add a listener to enable add button)
15006         * @param {Roo.bootstrap.ComboBox} combo This combo box
15007         */
15008         'add' : true,
15009         /**
15010          * @event edit
15011          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
15012         * @param {Roo.bootstrap.ComboBox} combo This combo box
15013         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
15014         */
15015         'edit' : true,
15016         /**
15017          * @event remove
15018          * Fires when the remove value from the combobox array
15019         * @param {Roo.bootstrap.ComboBox} combo This combo box
15020         */
15021         'remove' : true,
15022         /**
15023          * @event afterremove
15024          * Fires when the remove value from the combobox array
15025         * @param {Roo.bootstrap.ComboBox} combo This combo box
15026         */
15027         'afterremove' : true,
15028         /**
15029          * @event specialfilter
15030          * Fires when specialfilter
15031             * @param {Roo.bootstrap.ComboBox} combo This combo box
15032             */
15033         'specialfilter' : true,
15034         /**
15035          * @event tick
15036          * Fires when tick the element
15037             * @param {Roo.bootstrap.ComboBox} combo This combo box
15038             */
15039         'tick' : true,
15040         /**
15041          * @event touchviewdisplay
15042          * Fires when touch view require special display (default is using displayField)
15043             * @param {Roo.bootstrap.ComboBox} combo This combo box
15044             * @param {Object} cfg set html .
15045             */
15046         'touchviewdisplay' : true
15047         
15048     });
15049     
15050     this.item = [];
15051     this.tickItems = [];
15052     
15053     this.selectedIndex = -1;
15054     if(this.mode == 'local'){
15055         if(config.queryDelay === undefined){
15056             this.queryDelay = 10;
15057         }
15058         if(config.minChars === undefined){
15059             this.minChars = 0;
15060         }
15061     }
15062 };
15063
15064 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15065      
15066     /**
15067      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15068      * rendering into an Roo.Editor, defaults to false)
15069      */
15070     /**
15071      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15072      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15073      */
15074     /**
15075      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15076      */
15077     /**
15078      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15079      * the dropdown list (defaults to undefined, with no header element)
15080      */
15081
15082      /**
15083      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15084      */
15085      
15086      /**
15087      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15088      */
15089     listWidth: undefined,
15090     /**
15091      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15092      * mode = 'remote' or 'text' if mode = 'local')
15093      */
15094     displayField: undefined,
15095     
15096     /**
15097      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15098      * mode = 'remote' or 'value' if mode = 'local'). 
15099      * Note: use of a valueField requires the user make a selection
15100      * in order for a value to be mapped.
15101      */
15102     valueField: undefined,
15103     /**
15104      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15105      */
15106     modalTitle : '',
15107     
15108     /**
15109      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15110      * field's data value (defaults to the underlying DOM element's name)
15111      */
15112     hiddenName: undefined,
15113     /**
15114      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15115      */
15116     listClass: '',
15117     /**
15118      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15119      */
15120     selectedClass: 'active',
15121     
15122     /**
15123      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15124      */
15125     shadow:'sides',
15126     /**
15127      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15128      * anchor positions (defaults to 'tl-bl')
15129      */
15130     listAlign: 'tl-bl?',
15131     /**
15132      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15133      */
15134     maxHeight: 300,
15135     /**
15136      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15137      * query specified by the allQuery config option (defaults to 'query')
15138      */
15139     triggerAction: 'query',
15140     /**
15141      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15142      * (defaults to 4, does not apply if editable = false)
15143      */
15144     minChars : 4,
15145     /**
15146      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15147      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15148      */
15149     typeAhead: false,
15150     /**
15151      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15152      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15153      */
15154     queryDelay: 500,
15155     /**
15156      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15157      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15158      */
15159     pageSize: 0,
15160     /**
15161      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15162      * when editable = true (defaults to false)
15163      */
15164     selectOnFocus:false,
15165     /**
15166      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15167      */
15168     queryParam: 'query',
15169     /**
15170      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15171      * when mode = 'remote' (defaults to 'Loading...')
15172      */
15173     loadingText: 'Loading...',
15174     /**
15175      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15176      */
15177     resizable: false,
15178     /**
15179      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15180      */
15181     handleHeight : 8,
15182     /**
15183      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15184      * traditional select (defaults to true)
15185      */
15186     editable: true,
15187     /**
15188      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15189      */
15190     allQuery: '',
15191     /**
15192      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15193      */
15194     mode: 'remote',
15195     /**
15196      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15197      * listWidth has a higher value)
15198      */
15199     minListWidth : 70,
15200     /**
15201      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15202      * allow the user to set arbitrary text into the field (defaults to false)
15203      */
15204     forceSelection:false,
15205     /**
15206      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15207      * if typeAhead = true (defaults to 250)
15208      */
15209     typeAheadDelay : 250,
15210     /**
15211      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15212      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15213      */
15214     valueNotFoundText : undefined,
15215     /**
15216      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15217      */
15218     blockFocus : false,
15219     
15220     /**
15221      * @cfg {Boolean} disableClear Disable showing of clear button.
15222      */
15223     disableClear : false,
15224     /**
15225      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15226      */
15227     alwaysQuery : false,
15228     
15229     /**
15230      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15231      */
15232     multiple : false,
15233     
15234     /**
15235      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15236      */
15237     invalidClass : "has-warning",
15238     
15239     /**
15240      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15241      */
15242     validClass : "has-success",
15243     
15244     /**
15245      * @cfg {Boolean} specialFilter (true|false) special filter default false
15246      */
15247     specialFilter : false,
15248     
15249     /**
15250      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15251      */
15252     mobileTouchView : true,
15253     
15254     /**
15255      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15256      */
15257     useNativeIOS : false,
15258     
15259     /**
15260      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15261      */
15262     mobile_restrict_height : false,
15263     
15264     ios_options : false,
15265     
15266     //private
15267     addicon : false,
15268     editicon: false,
15269     
15270     page: 0,
15271     hasQuery: false,
15272     append: false,
15273     loadNext: false,
15274     autoFocus : true,
15275     tickable : false,
15276     btnPosition : 'right',
15277     triggerList : true,
15278     showToggleBtn : true,
15279     animate : true,
15280     emptyResultText: 'Empty',
15281     triggerText : 'Select',
15282     emptyTitle : '',
15283     width : false,
15284     
15285     // element that contains real text value.. (when hidden is used..)
15286     
15287     getAutoCreate : function()
15288     {   
15289         var cfg = false;
15290         //render
15291         /*
15292          * Render classic select for iso
15293          */
15294         
15295         if(Roo.isIOS && this.useNativeIOS){
15296             cfg = this.getAutoCreateNativeIOS();
15297             return cfg;
15298         }
15299         
15300         /*
15301          * Touch Devices
15302          */
15303         
15304         if(Roo.isTouch && this.mobileTouchView){
15305             cfg = this.getAutoCreateTouchView();
15306             return cfg;;
15307         }
15308         
15309         /*
15310          *  Normal ComboBox
15311          */
15312         if(!this.tickable){
15313             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15314             return cfg;
15315         }
15316         
15317         /*
15318          *  ComboBox with tickable selections
15319          */
15320              
15321         var align = this.labelAlign || this.parentLabelAlign();
15322         
15323         cfg = {
15324             cls : 'form-group roo-combobox-tickable' //input-group
15325         };
15326         
15327         var btn_text_select = '';
15328         var btn_text_done = '';
15329         var btn_text_cancel = '';
15330         
15331         if (this.btn_text_show) {
15332             btn_text_select = 'Select';
15333             btn_text_done = 'Done';
15334             btn_text_cancel = 'Cancel'; 
15335         }
15336         
15337         var buttons = {
15338             tag : 'div',
15339             cls : 'tickable-buttons',
15340             cn : [
15341                 {
15342                     tag : 'button',
15343                     type : 'button',
15344                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15345                     //html : this.triggerText
15346                     html: btn_text_select
15347                 },
15348                 {
15349                     tag : 'button',
15350                     type : 'button',
15351                     name : 'ok',
15352                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15353                     //html : 'Done'
15354                     html: btn_text_done
15355                 },
15356                 {
15357                     tag : 'button',
15358                     type : 'button',
15359                     name : 'cancel',
15360                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15361                     //html : 'Cancel'
15362                     html: btn_text_cancel
15363                 }
15364             ]
15365         };
15366         
15367         if(this.editable){
15368             buttons.cn.unshift({
15369                 tag: 'input',
15370                 cls: 'roo-select2-search-field-input'
15371             });
15372         }
15373         
15374         var _this = this;
15375         
15376         Roo.each(buttons.cn, function(c){
15377             if (_this.size) {
15378                 c.cls += ' btn-' + _this.size;
15379             }
15380
15381             if (_this.disabled) {
15382                 c.disabled = true;
15383             }
15384         });
15385         
15386         var box = {
15387             tag: 'div',
15388             style : 'display: contents',
15389             cn: [
15390                 {
15391                     tag: 'input',
15392                     type : 'hidden',
15393                     cls: 'form-hidden-field'
15394                 },
15395                 {
15396                     tag: 'ul',
15397                     cls: 'roo-select2-choices',
15398                     cn:[
15399                         {
15400                             tag: 'li',
15401                             cls: 'roo-select2-search-field',
15402                             cn: [
15403                                 buttons
15404                             ]
15405                         }
15406                     ]
15407                 }
15408             ]
15409         };
15410         
15411         var combobox = {
15412             cls: 'roo-select2-container input-group roo-select2-container-multi',
15413             cn: [
15414                 
15415                 box
15416 //                {
15417 //                    tag: 'ul',
15418 //                    cls: 'typeahead typeahead-long dropdown-menu',
15419 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15420 //                }
15421             ]
15422         };
15423         
15424         if(this.hasFeedback && !this.allowBlank){
15425             
15426             var feedback = {
15427                 tag: 'span',
15428                 cls: 'glyphicon form-control-feedback'
15429             };
15430
15431             combobox.cn.push(feedback);
15432         }
15433         
15434         
15435         
15436         var indicator = {
15437             tag : 'i',
15438             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15439             tooltip : 'This field is required'
15440         };
15441         if (Roo.bootstrap.version == 4) {
15442             indicator = {
15443                 tag : 'i',
15444                 style : 'display:none'
15445             };
15446         }
15447         if (align ==='left' && this.fieldLabel.length) {
15448             
15449             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15450             
15451             cfg.cn = [
15452                 indicator,
15453                 {
15454                     tag: 'label',
15455                     'for' :  id,
15456                     cls : 'control-label col-form-label',
15457                     html : this.fieldLabel
15458
15459                 },
15460                 {
15461                     cls : "", 
15462                     cn: [
15463                         combobox
15464                     ]
15465                 }
15466
15467             ];
15468             
15469             var labelCfg = cfg.cn[1];
15470             var contentCfg = cfg.cn[2];
15471             
15472
15473             if(this.indicatorpos == 'right'){
15474                 
15475                 cfg.cn = [
15476                     {
15477                         tag: 'label',
15478                         'for' :  id,
15479                         cls : 'control-label col-form-label',
15480                         cn : [
15481                             {
15482                                 tag : 'span',
15483                                 html : this.fieldLabel
15484                             },
15485                             indicator
15486                         ]
15487                     },
15488                     {
15489                         cls : "",
15490                         cn: [
15491                             combobox
15492                         ]
15493                     }
15494
15495                 ];
15496                 
15497                 
15498                 
15499                 labelCfg = cfg.cn[0];
15500                 contentCfg = cfg.cn[1];
15501             
15502             }
15503             
15504             if(this.labelWidth > 12){
15505                 labelCfg.style = "width: " + this.labelWidth + 'px';
15506             }
15507             if(this.width * 1 > 0){
15508                 contentCfg.style = "width: " + this.width + 'px';
15509             }
15510             if(this.labelWidth < 13 && this.labelmd == 0){
15511                 this.labelmd = this.labelWidth;
15512             }
15513             
15514             if(this.labellg > 0){
15515                 labelCfg.cls += ' col-lg-' + this.labellg;
15516                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15517             }
15518             
15519             if(this.labelmd > 0){
15520                 labelCfg.cls += ' col-md-' + this.labelmd;
15521                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15522             }
15523             
15524             if(this.labelsm > 0){
15525                 labelCfg.cls += ' col-sm-' + this.labelsm;
15526                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15527             }
15528             
15529             if(this.labelxs > 0){
15530                 labelCfg.cls += ' col-xs-' + this.labelxs;
15531                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15532             }
15533                 
15534                 
15535         } else if ( this.fieldLabel.length) {
15536 //                Roo.log(" label");
15537                  cfg.cn = [
15538                    indicator,
15539                     {
15540                         tag: 'label',
15541                         //cls : 'input-group-addon',
15542                         html : this.fieldLabel
15543                     },
15544                     combobox
15545                 ];
15546                 
15547                 if(this.indicatorpos == 'right'){
15548                     cfg.cn = [
15549                         {
15550                             tag: 'label',
15551                             //cls : 'input-group-addon',
15552                             html : this.fieldLabel
15553                         },
15554                         indicator,
15555                         combobox
15556                     ];
15557                     
15558                 }
15559
15560         } else {
15561             
15562 //                Roo.log(" no label && no align");
15563                 cfg = combobox
15564                      
15565                 
15566         }
15567          
15568         var settings=this;
15569         ['xs','sm','md','lg'].map(function(size){
15570             if (settings[size]) {
15571                 cfg.cls += ' col-' + size + '-' + settings[size];
15572             }
15573         });
15574         
15575         return cfg;
15576         
15577     },
15578     
15579     _initEventsCalled : false,
15580     
15581     // private
15582     initEvents: function()
15583     {   
15584         if (this._initEventsCalled) { // as we call render... prevent looping...
15585             return;
15586         }
15587         this._initEventsCalled = true;
15588         
15589         if (!this.store) {
15590             throw "can not find store for combo";
15591         }
15592         
15593         this.indicator = this.indicatorEl();
15594         
15595         this.store = Roo.factory(this.store, Roo.data);
15596         this.store.parent = this;
15597         
15598         // if we are building from html. then this element is so complex, that we can not really
15599         // use the rendered HTML.
15600         // so we have to trash and replace the previous code.
15601         if (Roo.XComponent.build_from_html) {
15602             // remove this element....
15603             var e = this.el.dom, k=0;
15604             while (e ) { e = e.previousSibling;  ++k;}
15605
15606             this.el.remove();
15607             
15608             this.el=false;
15609             this.rendered = false;
15610             
15611             this.render(this.parent().getChildContainer(true), k);
15612         }
15613         
15614         if(Roo.isIOS && this.useNativeIOS){
15615             this.initIOSView();
15616             return;
15617         }
15618         
15619         /*
15620          * Touch Devices
15621          */
15622         
15623         if(Roo.isTouch && this.mobileTouchView){
15624             this.initTouchView();
15625             return;
15626         }
15627         
15628         if(this.tickable){
15629             this.initTickableEvents();
15630             return;
15631         }
15632         
15633         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15634         
15635         if(this.hiddenName){
15636             
15637             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15638             
15639             this.hiddenField.dom.value =
15640                 this.hiddenValue !== undefined ? this.hiddenValue :
15641                 this.value !== undefined ? this.value : '';
15642
15643             // prevent input submission
15644             this.el.dom.removeAttribute('name');
15645             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15646              
15647              
15648         }
15649         //if(Roo.isGecko){
15650         //    this.el.dom.setAttribute('autocomplete', 'off');
15651         //}
15652         
15653         var cls = 'x-combo-list';
15654         
15655         //this.list = new Roo.Layer({
15656         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15657         //});
15658         
15659         var _this = this;
15660         
15661         (function(){
15662             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15663             _this.list.setWidth(lw);
15664         }).defer(100);
15665         
15666         this.list.on('mouseover', this.onViewOver, this);
15667         this.list.on('mousemove', this.onViewMove, this);
15668         this.list.on('scroll', this.onViewScroll, this);
15669         
15670         /*
15671         this.list.swallowEvent('mousewheel');
15672         this.assetHeight = 0;
15673
15674         if(this.title){
15675             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15676             this.assetHeight += this.header.getHeight();
15677         }
15678
15679         this.innerList = this.list.createChild({cls:cls+'-inner'});
15680         this.innerList.on('mouseover', this.onViewOver, this);
15681         this.innerList.on('mousemove', this.onViewMove, this);
15682         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15683         
15684         if(this.allowBlank && !this.pageSize && !this.disableClear){
15685             this.footer = this.list.createChild({cls:cls+'-ft'});
15686             this.pageTb = new Roo.Toolbar(this.footer);
15687            
15688         }
15689         if(this.pageSize){
15690             this.footer = this.list.createChild({cls:cls+'-ft'});
15691             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15692                     {pageSize: this.pageSize});
15693             
15694         }
15695         
15696         if (this.pageTb && this.allowBlank && !this.disableClear) {
15697             var _this = this;
15698             this.pageTb.add(new Roo.Toolbar.Fill(), {
15699                 cls: 'x-btn-icon x-btn-clear',
15700                 text: '&#160;',
15701                 handler: function()
15702                 {
15703                     _this.collapse();
15704                     _this.clearValue();
15705                     _this.onSelect(false, -1);
15706                 }
15707             });
15708         }
15709         if (this.footer) {
15710             this.assetHeight += this.footer.getHeight();
15711         }
15712         */
15713             
15714         if(!this.tpl){
15715             this.tpl = Roo.bootstrap.version == 4 ?
15716                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15717                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15718         }
15719
15720         this.view = new Roo.View(this.list, this.tpl, {
15721             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15722         });
15723         //this.view.wrapEl.setDisplayed(false);
15724         this.view.on('click', this.onViewClick, this);
15725         
15726         
15727         this.store.on('beforeload', this.onBeforeLoad, this);
15728         this.store.on('load', this.onLoad, this);
15729         this.store.on('loadexception', this.onLoadException, this);
15730         /*
15731         if(this.resizable){
15732             this.resizer = new Roo.Resizable(this.list,  {
15733                pinned:true, handles:'se'
15734             });
15735             this.resizer.on('resize', function(r, w, h){
15736                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15737                 this.listWidth = w;
15738                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15739                 this.restrictHeight();
15740             }, this);
15741             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15742         }
15743         */
15744         if(!this.editable){
15745             this.editable = true;
15746             this.setEditable(false);
15747         }
15748         
15749         /*
15750         
15751         if (typeof(this.events.add.listeners) != 'undefined') {
15752             
15753             this.addicon = this.wrap.createChild(
15754                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15755        
15756             this.addicon.on('click', function(e) {
15757                 this.fireEvent('add', this);
15758             }, this);
15759         }
15760         if (typeof(this.events.edit.listeners) != 'undefined') {
15761             
15762             this.editicon = this.wrap.createChild(
15763                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15764             if (this.addicon) {
15765                 this.editicon.setStyle('margin-left', '40px');
15766             }
15767             this.editicon.on('click', function(e) {
15768                 
15769                 // we fire even  if inothing is selected..
15770                 this.fireEvent('edit', this, this.lastData );
15771                 
15772             }, this);
15773         }
15774         */
15775         
15776         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15777             "up" : function(e){
15778                 this.inKeyMode = true;
15779                 this.selectPrev();
15780             },
15781
15782             "down" : function(e){
15783                 if(!this.isExpanded()){
15784                     this.onTriggerClick();
15785                 }else{
15786                     this.inKeyMode = true;
15787                     this.selectNext();
15788                 }
15789             },
15790
15791             "enter" : function(e){
15792 //                this.onViewClick();
15793                 //return true;
15794                 this.collapse();
15795                 
15796                 if(this.fireEvent("specialkey", this, e)){
15797                     this.onViewClick(false);
15798                 }
15799                 
15800                 return true;
15801             },
15802
15803             "esc" : function(e){
15804                 this.collapse();
15805             },
15806
15807             "tab" : function(e){
15808                 this.collapse();
15809                 
15810                 if(this.fireEvent("specialkey", this, e)){
15811                     this.onViewClick(false);
15812                 }
15813                 
15814                 return true;
15815             },
15816
15817             scope : this,
15818
15819             doRelay : function(foo, bar, hname){
15820                 if(hname == 'down' || this.scope.isExpanded()){
15821                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15822                 }
15823                 return true;
15824             },
15825
15826             forceKeyDown: true
15827         });
15828         
15829         
15830         this.queryDelay = Math.max(this.queryDelay || 10,
15831                 this.mode == 'local' ? 10 : 250);
15832         
15833         
15834         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15835         
15836         if(this.typeAhead){
15837             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15838         }
15839         if(this.editable !== false){
15840             this.inputEl().on("keyup", this.onKeyUp, this);
15841         }
15842         if(this.forceSelection){
15843             this.inputEl().on('blur', this.doForce, this);
15844         }
15845         
15846         if(this.multiple){
15847             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15848             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15849         }
15850     },
15851     
15852     initTickableEvents: function()
15853     {   
15854         this.createList();
15855         
15856         if(this.hiddenName){
15857             
15858             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15859             
15860             this.hiddenField.dom.value =
15861                 this.hiddenValue !== undefined ? this.hiddenValue :
15862                 this.value !== undefined ? this.value : '';
15863
15864             // prevent input submission
15865             this.el.dom.removeAttribute('name');
15866             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15867              
15868              
15869         }
15870         
15871 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15872         
15873         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15874         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15875         if(this.triggerList){
15876             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15877         }
15878          
15879         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15880         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15881         
15882         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15883         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15884         
15885         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15886         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15887         
15888         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15889         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15890         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15891         
15892         this.okBtn.hide();
15893         this.cancelBtn.hide();
15894         
15895         var _this = this;
15896         
15897         (function(){
15898             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15899             _this.list.setWidth(lw);
15900         }).defer(100);
15901         
15902         this.list.on('mouseover', this.onViewOver, this);
15903         this.list.on('mousemove', this.onViewMove, this);
15904         
15905         this.list.on('scroll', this.onViewScroll, this);
15906         
15907         if(!this.tpl){
15908             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15909                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15910         }
15911
15912         this.view = new Roo.View(this.list, this.tpl, {
15913             singleSelect:true,
15914             tickable:true,
15915             parent:this,
15916             store: this.store,
15917             selectedClass: this.selectedClass
15918         });
15919         
15920         //this.view.wrapEl.setDisplayed(false);
15921         this.view.on('click', this.onViewClick, this);
15922         
15923         
15924         
15925         this.store.on('beforeload', this.onBeforeLoad, this);
15926         this.store.on('load', this.onLoad, this);
15927         this.store.on('loadexception', this.onLoadException, this);
15928         
15929         if(this.editable){
15930             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15931                 "up" : function(e){
15932                     this.inKeyMode = true;
15933                     this.selectPrev();
15934                 },
15935
15936                 "down" : function(e){
15937                     this.inKeyMode = true;
15938                     this.selectNext();
15939                 },
15940
15941                 "enter" : function(e){
15942                     if(this.fireEvent("specialkey", this, e)){
15943                         this.onViewClick(false);
15944                     }
15945                     
15946                     return true;
15947                 },
15948
15949                 "esc" : function(e){
15950                     this.onTickableFooterButtonClick(e, false, false);
15951                 },
15952
15953                 "tab" : function(e){
15954                     this.fireEvent("specialkey", this, e);
15955                     
15956                     this.onTickableFooterButtonClick(e, false, false);
15957                     
15958                     return true;
15959                 },
15960
15961                 scope : this,
15962
15963                 doRelay : function(e, fn, key){
15964                     if(this.scope.isExpanded()){
15965                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15966                     }
15967                     return true;
15968                 },
15969
15970                 forceKeyDown: true
15971             });
15972         }
15973         
15974         this.queryDelay = Math.max(this.queryDelay || 10,
15975                 this.mode == 'local' ? 10 : 250);
15976         
15977         
15978         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15979         
15980         if(this.typeAhead){
15981             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15982         }
15983         
15984         if(this.editable !== false){
15985             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15986         }
15987         
15988         this.indicator = this.indicatorEl();
15989         
15990         if(this.indicator){
15991             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15992             this.indicator.hide();
15993         }
15994         
15995     },
15996
15997     onDestroy : function(){
15998         if(this.view){
15999             this.view.setStore(null);
16000             this.view.el.removeAllListeners();
16001             this.view.el.remove();
16002             this.view.purgeListeners();
16003         }
16004         if(this.list){
16005             this.list.dom.innerHTML  = '';
16006         }
16007         
16008         if(this.store){
16009             this.store.un('beforeload', this.onBeforeLoad, this);
16010             this.store.un('load', this.onLoad, this);
16011             this.store.un('loadexception', this.onLoadException, this);
16012         }
16013         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
16014     },
16015
16016     // private
16017     fireKey : function(e){
16018         if(e.isNavKeyPress() && !this.list.isVisible()){
16019             this.fireEvent("specialkey", this, e);
16020         }
16021     },
16022
16023     // private
16024     onResize: function(w, h)
16025     {
16026         
16027         
16028 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16029 //        
16030 //        if(typeof w != 'number'){
16031 //            // we do not handle it!?!?
16032 //            return;
16033 //        }
16034 //        var tw = this.trigger.getWidth();
16035 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16036 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16037 //        var x = w - tw;
16038 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16039 //            
16040 //        //this.trigger.setStyle('left', x+'px');
16041 //        
16042 //        if(this.list && this.listWidth === undefined){
16043 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16044 //            this.list.setWidth(lw);
16045 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16046 //        }
16047         
16048     
16049         
16050     },
16051
16052     /**
16053      * Allow or prevent the user from directly editing the field text.  If false is passed,
16054      * the user will only be able to select from the items defined in the dropdown list.  This method
16055      * is the runtime equivalent of setting the 'editable' config option at config time.
16056      * @param {Boolean} value True to allow the user to directly edit the field text
16057      */
16058     setEditable : function(value){
16059         if(value == this.editable){
16060             return;
16061         }
16062         this.editable = value;
16063         if(!value){
16064             this.inputEl().dom.setAttribute('readOnly', true);
16065             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16066             this.inputEl().addClass('x-combo-noedit');
16067         }else{
16068             this.inputEl().dom.setAttribute('readOnly', false);
16069             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16070             this.inputEl().removeClass('x-combo-noedit');
16071         }
16072     },
16073
16074     // private
16075     
16076     onBeforeLoad : function(combo,opts){
16077         if(!this.hasFocus){
16078             return;
16079         }
16080          if (!opts.add) {
16081             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16082          }
16083         this.restrictHeight();
16084         this.selectedIndex = -1;
16085     },
16086
16087     // private
16088     onLoad : function(){
16089         
16090         this.hasQuery = false;
16091         
16092         if(!this.hasFocus){
16093             return;
16094         }
16095         
16096         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16097             this.loading.hide();
16098         }
16099         
16100         if(this.store.getCount() > 0){
16101             
16102             this.expand();
16103             this.restrictHeight();
16104             if(this.lastQuery == this.allQuery){
16105                 if(this.editable && !this.tickable){
16106                     this.inputEl().dom.select();
16107                 }
16108                 
16109                 if(
16110                     !this.selectByValue(this.value, true) &&
16111                     this.autoFocus && 
16112                     (
16113                         !this.store.lastOptions ||
16114                         typeof(this.store.lastOptions.add) == 'undefined' || 
16115                         this.store.lastOptions.add != true
16116                     )
16117                 ){
16118                     this.select(0, true);
16119                 }
16120             }else{
16121                 if(this.autoFocus){
16122                     this.selectNext();
16123                 }
16124                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16125                     this.taTask.delay(this.typeAheadDelay);
16126                 }
16127             }
16128         }else{
16129             this.onEmptyResults();
16130         }
16131         
16132         //this.el.focus();
16133     },
16134     // private
16135     onLoadException : function()
16136     {
16137         this.hasQuery = false;
16138         
16139         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16140             this.loading.hide();
16141         }
16142         
16143         if(this.tickable && this.editable){
16144             return;
16145         }
16146         
16147         this.collapse();
16148         // only causes errors at present
16149         //Roo.log(this.store.reader.jsonData);
16150         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16151             // fixme
16152             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16153         //}
16154         
16155         
16156     },
16157     // private
16158     onTypeAhead : function(){
16159         if(this.store.getCount() > 0){
16160             var r = this.store.getAt(0);
16161             var newValue = r.data[this.displayField];
16162             var len = newValue.length;
16163             var selStart = this.getRawValue().length;
16164             
16165             if(selStart != len){
16166                 this.setRawValue(newValue);
16167                 this.selectText(selStart, newValue.length);
16168             }
16169         }
16170     },
16171
16172     // private
16173     onSelect : function(record, index){
16174         
16175         if(this.fireEvent('beforeselect', this, record, index) !== false){
16176         
16177             this.setFromData(index > -1 ? record.data : false);
16178             
16179             this.collapse();
16180             this.fireEvent('select', this, record, index);
16181         }
16182     },
16183
16184     /**
16185      * Returns the currently selected field value or empty string if no value is set.
16186      * @return {String} value The selected value
16187      */
16188     getValue : function()
16189     {
16190         if(Roo.isIOS && this.useNativeIOS){
16191             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16192         }
16193         
16194         if(this.multiple){
16195             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16196         }
16197         
16198         if(this.valueField){
16199             return typeof this.value != 'undefined' ? this.value : '';
16200         }else{
16201             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16202         }
16203     },
16204     
16205     getRawValue : function()
16206     {
16207         if(Roo.isIOS && this.useNativeIOS){
16208             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16209         }
16210         
16211         var v = this.inputEl().getValue();
16212         
16213         return v;
16214     },
16215
16216     /**
16217      * Clears any text/value currently set in the field
16218      */
16219     clearValue : function(){
16220         
16221         if(this.hiddenField){
16222             this.hiddenField.dom.value = '';
16223         }
16224         this.value = '';
16225         this.setRawValue('');
16226         this.lastSelectionText = '';
16227         this.lastData = false;
16228         
16229         var close = this.closeTriggerEl();
16230         
16231         if(close){
16232             close.hide();
16233         }
16234         
16235         this.validate();
16236         
16237     },
16238
16239     /**
16240      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16241      * will be displayed in the field.  If the value does not match the data value of an existing item,
16242      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16243      * Otherwise the field will be blank (although the value will still be set).
16244      * @param {String} value The value to match
16245      */
16246     setValue : function(v)
16247     {
16248         if(Roo.isIOS && this.useNativeIOS){
16249             this.setIOSValue(v);
16250             return;
16251         }
16252         
16253         if(this.multiple){
16254             this.syncValue();
16255             return;
16256         }
16257         
16258         var text = v;
16259         if(this.valueField){
16260             var r = this.findRecord(this.valueField, v);
16261             if(r){
16262                 text = r.data[this.displayField];
16263             }else if(this.valueNotFoundText !== undefined){
16264                 text = this.valueNotFoundText;
16265             }
16266         }
16267         this.lastSelectionText = text;
16268         if(this.hiddenField){
16269             this.hiddenField.dom.value = v;
16270         }
16271         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16272         this.value = v;
16273         
16274         var close = this.closeTriggerEl();
16275         
16276         if(close){
16277             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16278         }
16279         
16280         this.validate();
16281     },
16282     /**
16283      * @property {Object} the last set data for the element
16284      */
16285     
16286     lastData : false,
16287     /**
16288      * Sets the value of the field based on a object which is related to the record format for the store.
16289      * @param {Object} value the value to set as. or false on reset?
16290      */
16291     setFromData : function(o){
16292         
16293         if(this.multiple){
16294             this.addItem(o);
16295             return;
16296         }
16297             
16298         var dv = ''; // display value
16299         var vv = ''; // value value..
16300         this.lastData = o;
16301         if (this.displayField) {
16302             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16303         } else {
16304             // this is an error condition!!!
16305             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16306         }
16307         
16308         if(this.valueField){
16309             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16310         }
16311         
16312         var close = this.closeTriggerEl();
16313         
16314         if(close){
16315             if(dv.length || vv * 1 > 0){
16316                 close.show() ;
16317                 this.blockFocus=true;
16318             } else {
16319                 close.hide();
16320             }             
16321         }
16322         
16323         if(this.hiddenField){
16324             this.hiddenField.dom.value = vv;
16325             
16326             this.lastSelectionText = dv;
16327             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16328             this.value = vv;
16329             return;
16330         }
16331         // no hidden field.. - we store the value in 'value', but still display
16332         // display field!!!!
16333         this.lastSelectionText = dv;
16334         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16335         this.value = vv;
16336         
16337         
16338         
16339     },
16340     // private
16341     reset : function(){
16342         // overridden so that last data is reset..
16343         
16344         if(this.multiple){
16345             this.clearItem();
16346             return;
16347         }
16348         
16349         this.setValue(this.originalValue);
16350         //this.clearInvalid();
16351         this.lastData = false;
16352         if (this.view) {
16353             this.view.clearSelections();
16354         }
16355         
16356         this.validate();
16357     },
16358     // private
16359     findRecord : function(prop, value){
16360         var record;
16361         if(this.store.getCount() > 0){
16362             this.store.each(function(r){
16363                 if(r.data[prop] == value){
16364                     record = r;
16365                     return false;
16366                 }
16367                 return true;
16368             });
16369         }
16370         return record;
16371     },
16372     
16373     getName: function()
16374     {
16375         // returns hidden if it's set..
16376         if (!this.rendered) {return ''};
16377         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16378         
16379     },
16380     // private
16381     onViewMove : function(e, t){
16382         this.inKeyMode = false;
16383     },
16384
16385     // private
16386     onViewOver : function(e, t){
16387         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16388             return;
16389         }
16390         var item = this.view.findItemFromChild(t);
16391         
16392         if(item){
16393             var index = this.view.indexOf(item);
16394             this.select(index, false);
16395         }
16396     },
16397
16398     // private
16399     onViewClick : function(view, doFocus, el, e)
16400     {
16401         var index = this.view.getSelectedIndexes()[0];
16402         
16403         var r = this.store.getAt(index);
16404         
16405         if(this.tickable){
16406             
16407             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16408                 return;
16409             }
16410             
16411             var rm = false;
16412             var _this = this;
16413             
16414             Roo.each(this.tickItems, function(v,k){
16415                 
16416                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16417                     Roo.log(v);
16418                     _this.tickItems.splice(k, 1);
16419                     
16420                     if(typeof(e) == 'undefined' && view == false){
16421                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16422                     }
16423                     
16424                     rm = true;
16425                     return;
16426                 }
16427             });
16428             
16429             if(rm){
16430                 return;
16431             }
16432             
16433             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16434                 this.tickItems.push(r.data);
16435             }
16436             
16437             if(typeof(e) == 'undefined' && view == false){
16438                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16439             }
16440                     
16441             return;
16442         }
16443         
16444         if(r){
16445             this.onSelect(r, index);
16446         }
16447         if(doFocus !== false && !this.blockFocus){
16448             this.inputEl().focus();
16449         }
16450     },
16451
16452     // private
16453     restrictHeight : function(){
16454         //this.innerList.dom.style.height = '';
16455         //var inner = this.innerList.dom;
16456         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16457         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16458         //this.list.beginUpdate();
16459         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16460         this.list.alignTo(this.inputEl(), this.listAlign);
16461         this.list.alignTo(this.inputEl(), this.listAlign);
16462         //this.list.endUpdate();
16463     },
16464
16465     // private
16466     onEmptyResults : function(){
16467         
16468         if(this.tickable && this.editable){
16469             this.hasFocus = false;
16470             this.restrictHeight();
16471             return;
16472         }
16473         
16474         this.collapse();
16475     },
16476
16477     /**
16478      * Returns true if the dropdown list is expanded, else false.
16479      */
16480     isExpanded : function(){
16481         return this.list.isVisible();
16482     },
16483
16484     /**
16485      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16486      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16487      * @param {String} value The data value of the item to select
16488      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16489      * selected item if it is not currently in view (defaults to true)
16490      * @return {Boolean} True if the value matched an item in the list, else false
16491      */
16492     selectByValue : function(v, scrollIntoView){
16493         if(v !== undefined && v !== null){
16494             var r = this.findRecord(this.valueField || this.displayField, v);
16495             if(r){
16496                 this.select(this.store.indexOf(r), scrollIntoView);
16497                 return true;
16498             }
16499         }
16500         return false;
16501     },
16502
16503     /**
16504      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16505      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16506      * @param {Number} index The zero-based index of the list item to select
16507      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16508      * selected item if it is not currently in view (defaults to true)
16509      */
16510     select : function(index, scrollIntoView){
16511         this.selectedIndex = index;
16512         this.view.select(index);
16513         if(scrollIntoView !== false){
16514             var el = this.view.getNode(index);
16515             /*
16516              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16517              */
16518             if(el){
16519                 this.list.scrollChildIntoView(el, false);
16520             }
16521         }
16522     },
16523
16524     // private
16525     selectNext : function(){
16526         var ct = this.store.getCount();
16527         if(ct > 0){
16528             if(this.selectedIndex == -1){
16529                 this.select(0);
16530             }else if(this.selectedIndex < ct-1){
16531                 this.select(this.selectedIndex+1);
16532             }
16533         }
16534     },
16535
16536     // private
16537     selectPrev : function(){
16538         var ct = this.store.getCount();
16539         if(ct > 0){
16540             if(this.selectedIndex == -1){
16541                 this.select(0);
16542             }else if(this.selectedIndex != 0){
16543                 this.select(this.selectedIndex-1);
16544             }
16545         }
16546     },
16547
16548     // private
16549     onKeyUp : function(e){
16550         if(this.editable !== false && !e.isSpecialKey()){
16551             this.lastKey = e.getKey();
16552             this.dqTask.delay(this.queryDelay);
16553         }
16554     },
16555
16556     // private
16557     validateBlur : function(){
16558         return !this.list || !this.list.isVisible();   
16559     },
16560
16561     // private
16562     initQuery : function(){
16563         
16564         var v = this.getRawValue();
16565         
16566         if(this.tickable && this.editable){
16567             v = this.tickableInputEl().getValue();
16568         }
16569         
16570         this.doQuery(v);
16571     },
16572
16573     // private
16574     doForce : function(){
16575         if(this.inputEl().dom.value.length > 0){
16576             this.inputEl().dom.value =
16577                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16578              
16579         }
16580     },
16581
16582     /**
16583      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16584      * query allowing the query action to be canceled if needed.
16585      * @param {String} query The SQL query to execute
16586      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16587      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16588      * saved in the current store (defaults to false)
16589      */
16590     doQuery : function(q, forceAll){
16591         
16592         if(q === undefined || q === null){
16593             q = '';
16594         }
16595         var qe = {
16596             query: q,
16597             forceAll: forceAll,
16598             combo: this,
16599             cancel:false
16600         };
16601         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16602             return false;
16603         }
16604         q = qe.query;
16605         
16606         forceAll = qe.forceAll;
16607         if(forceAll === true || (q.length >= this.minChars)){
16608             
16609             this.hasQuery = true;
16610             
16611             if(this.lastQuery != q || this.alwaysQuery){
16612                 this.lastQuery = q;
16613                 if(this.mode == 'local'){
16614                     this.selectedIndex = -1;
16615                     if(forceAll){
16616                         this.store.clearFilter();
16617                     }else{
16618                         
16619                         if(this.specialFilter){
16620                             this.fireEvent('specialfilter', this);
16621                             this.onLoad();
16622                             return;
16623                         }
16624                         
16625                         this.store.filter(this.displayField, q);
16626                     }
16627                     
16628                     this.store.fireEvent("datachanged", this.store);
16629                     
16630                     this.onLoad();
16631                     
16632                     
16633                 }else{
16634                     
16635                     this.store.baseParams[this.queryParam] = q;
16636                     
16637                     var options = {params : this.getParams(q)};
16638                     
16639                     if(this.loadNext){
16640                         options.add = true;
16641                         options.params.start = this.page * this.pageSize;
16642                     }
16643                     
16644                     this.store.load(options);
16645                     
16646                     /*
16647                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16648                      *  we should expand the list on onLoad
16649                      *  so command out it
16650                      */
16651 //                    this.expand();
16652                 }
16653             }else{
16654                 this.selectedIndex = -1;
16655                 this.onLoad();   
16656             }
16657         }
16658         
16659         this.loadNext = false;
16660     },
16661     
16662     // private
16663     getParams : function(q){
16664         var p = {};
16665         //p[this.queryParam] = q;
16666         
16667         if(this.pageSize){
16668             p.start = 0;
16669             p.limit = this.pageSize;
16670         }
16671         return p;
16672     },
16673
16674     /**
16675      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16676      */
16677     collapse : function(){
16678         if(!this.isExpanded()){
16679             return;
16680         }
16681         
16682         this.list.hide();
16683         
16684         this.hasFocus = false;
16685         
16686         if(this.tickable){
16687             this.okBtn.hide();
16688             this.cancelBtn.hide();
16689             this.trigger.show();
16690             
16691             if(this.editable){
16692                 this.tickableInputEl().dom.value = '';
16693                 this.tickableInputEl().blur();
16694             }
16695             
16696         }
16697         
16698         Roo.get(document).un('mousedown', this.collapseIf, this);
16699         Roo.get(document).un('mousewheel', this.collapseIf, this);
16700         if (!this.editable) {
16701             Roo.get(document).un('keydown', this.listKeyPress, this);
16702         }
16703         this.fireEvent('collapse', this);
16704         
16705         this.validate();
16706     },
16707
16708     // private
16709     collapseIf : function(e){
16710         var in_combo  = e.within(this.el);
16711         var in_list =  e.within(this.list);
16712         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16713         
16714         if (in_combo || in_list || is_list) {
16715             //e.stopPropagation();
16716             return;
16717         }
16718         
16719         if(this.tickable){
16720             this.onTickableFooterButtonClick(e, false, false);
16721         }
16722
16723         this.collapse();
16724         
16725     },
16726
16727     /**
16728      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16729      */
16730     expand : function(){
16731        
16732         if(this.isExpanded() || !this.hasFocus){
16733             return;
16734         }
16735         
16736         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16737         this.list.setWidth(lw);
16738         
16739         Roo.log('expand');
16740         
16741         this.list.show();
16742         
16743         this.restrictHeight();
16744         
16745         if(this.tickable){
16746             
16747             this.tickItems = Roo.apply([], this.item);
16748             
16749             this.okBtn.show();
16750             this.cancelBtn.show();
16751             this.trigger.hide();
16752             
16753             if(this.editable){
16754                 this.tickableInputEl().focus();
16755             }
16756             
16757         }
16758         
16759         Roo.get(document).on('mousedown', this.collapseIf, this);
16760         Roo.get(document).on('mousewheel', this.collapseIf, this);
16761         if (!this.editable) {
16762             Roo.get(document).on('keydown', this.listKeyPress, this);
16763         }
16764         
16765         this.fireEvent('expand', this);
16766     },
16767
16768     // private
16769     // Implements the default empty TriggerField.onTriggerClick function
16770     onTriggerClick : function(e)
16771     {
16772         Roo.log('trigger click');
16773         
16774         if(this.disabled || !this.triggerList){
16775             return;
16776         }
16777         
16778         this.page = 0;
16779         this.loadNext = false;
16780         
16781         if(this.isExpanded()){
16782             this.collapse();
16783             if (!this.blockFocus) {
16784                 this.inputEl().focus();
16785             }
16786             
16787         }else {
16788             this.hasFocus = true;
16789             if(this.triggerAction == 'all') {
16790                 this.doQuery(this.allQuery, true);
16791             } else {
16792                 this.doQuery(this.getRawValue());
16793             }
16794             if (!this.blockFocus) {
16795                 this.inputEl().focus();
16796             }
16797         }
16798     },
16799     
16800     onTickableTriggerClick : function(e)
16801     {
16802         if(this.disabled){
16803             return;
16804         }
16805         
16806         this.page = 0;
16807         this.loadNext = false;
16808         this.hasFocus = true;
16809         
16810         if(this.triggerAction == 'all') {
16811             this.doQuery(this.allQuery, true);
16812         } else {
16813             this.doQuery(this.getRawValue());
16814         }
16815     },
16816     
16817     onSearchFieldClick : function(e)
16818     {
16819         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16820             this.onTickableFooterButtonClick(e, false, false);
16821             return;
16822         }
16823         
16824         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16825             return;
16826         }
16827         
16828         this.page = 0;
16829         this.loadNext = false;
16830         this.hasFocus = true;
16831         
16832         if(this.triggerAction == 'all') {
16833             this.doQuery(this.allQuery, true);
16834         } else {
16835             this.doQuery(this.getRawValue());
16836         }
16837     },
16838     
16839     listKeyPress : function(e)
16840     {
16841         //Roo.log('listkeypress');
16842         // scroll to first matching element based on key pres..
16843         if (e.isSpecialKey()) {
16844             return false;
16845         }
16846         var k = String.fromCharCode(e.getKey()).toUpperCase();
16847         //Roo.log(k);
16848         var match  = false;
16849         var csel = this.view.getSelectedNodes();
16850         var cselitem = false;
16851         if (csel.length) {
16852             var ix = this.view.indexOf(csel[0]);
16853             cselitem  = this.store.getAt(ix);
16854             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16855                 cselitem = false;
16856             }
16857             
16858         }
16859         
16860         this.store.each(function(v) { 
16861             if (cselitem) {
16862                 // start at existing selection.
16863                 if (cselitem.id == v.id) {
16864                     cselitem = false;
16865                 }
16866                 return true;
16867             }
16868                 
16869             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16870                 match = this.store.indexOf(v);
16871                 return false;
16872             }
16873             return true;
16874         }, this);
16875         
16876         if (match === false) {
16877             return true; // no more action?
16878         }
16879         // scroll to?
16880         this.view.select(match);
16881         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16882         sn.scrollIntoView(sn.dom.parentNode, false);
16883     },
16884     
16885     onViewScroll : function(e, t){
16886         
16887         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){
16888             return;
16889         }
16890         
16891         this.hasQuery = true;
16892         
16893         this.loading = this.list.select('.loading', true).first();
16894         
16895         if(this.loading === null){
16896             this.list.createChild({
16897                 tag: 'div',
16898                 cls: 'loading roo-select2-more-results roo-select2-active',
16899                 html: 'Loading more results...'
16900             });
16901             
16902             this.loading = this.list.select('.loading', true).first();
16903             
16904             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16905             
16906             this.loading.hide();
16907         }
16908         
16909         this.loading.show();
16910         
16911         var _combo = this;
16912         
16913         this.page++;
16914         this.loadNext = true;
16915         
16916         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16917         
16918         return;
16919     },
16920     
16921     addItem : function(o)
16922     {   
16923         var dv = ''; // display value
16924         
16925         if (this.displayField) {
16926             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16927         } else {
16928             // this is an error condition!!!
16929             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16930         }
16931         
16932         if(!dv.length){
16933             return;
16934         }
16935         
16936         var choice = this.choices.createChild({
16937             tag: 'li',
16938             cls: 'roo-select2-search-choice',
16939             cn: [
16940                 {
16941                     tag: 'div',
16942                     html: dv
16943                 },
16944                 {
16945                     tag: 'a',
16946                     href: '#',
16947                     cls: 'roo-select2-search-choice-close fa fa-times',
16948                     tabindex: '-1'
16949                 }
16950             ]
16951             
16952         }, this.searchField);
16953         
16954         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16955         
16956         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16957         
16958         this.item.push(o);
16959         
16960         this.lastData = o;
16961         
16962         this.syncValue();
16963         
16964         this.inputEl().dom.value = '';
16965         
16966         this.validate();
16967     },
16968     
16969     onRemoveItem : function(e, _self, o)
16970     {
16971         e.preventDefault();
16972         
16973         this.lastItem = Roo.apply([], this.item);
16974         
16975         var index = this.item.indexOf(o.data) * 1;
16976         
16977         if( index < 0){
16978             Roo.log('not this item?!');
16979             return;
16980         }
16981         
16982         this.item.splice(index, 1);
16983         o.item.remove();
16984         
16985         this.syncValue();
16986         
16987         this.fireEvent('remove', this, e);
16988         
16989         this.validate();
16990         
16991     },
16992     
16993     syncValue : function()
16994     {
16995         if(!this.item.length){
16996             this.clearValue();
16997             return;
16998         }
16999             
17000         var value = [];
17001         var _this = this;
17002         Roo.each(this.item, function(i){
17003             if(_this.valueField){
17004                 value.push(i[_this.valueField]);
17005                 return;
17006             }
17007
17008             value.push(i);
17009         });
17010
17011         this.value = value.join(',');
17012
17013         if(this.hiddenField){
17014             this.hiddenField.dom.value = this.value;
17015         }
17016         
17017         this.store.fireEvent("datachanged", this.store);
17018         
17019         this.validate();
17020     },
17021     
17022     clearItem : function()
17023     {
17024         if(!this.multiple){
17025             return;
17026         }
17027         
17028         this.item = [];
17029         
17030         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17031            c.remove();
17032         });
17033         
17034         this.syncValue();
17035         
17036         this.validate();
17037         
17038         if(this.tickable && !Roo.isTouch){
17039             this.view.refresh();
17040         }
17041     },
17042     
17043     inputEl: function ()
17044     {
17045         if(Roo.isIOS && this.useNativeIOS){
17046             return this.el.select('select.roo-ios-select', true).first();
17047         }
17048         
17049         if(Roo.isTouch && this.mobileTouchView){
17050             return this.el.select('input.form-control',true).first();
17051         }
17052         
17053         if(this.tickable){
17054             return this.searchField;
17055         }
17056         
17057         return this.el.select('input.form-control',true).first();
17058     },
17059     
17060     onTickableFooterButtonClick : function(e, btn, el)
17061     {
17062         e.preventDefault();
17063         
17064         this.lastItem = Roo.apply([], this.item);
17065         
17066         if(btn && btn.name == 'cancel'){
17067             this.tickItems = Roo.apply([], this.item);
17068             this.collapse();
17069             return;
17070         }
17071         
17072         this.clearItem();
17073         
17074         var _this = this;
17075         
17076         Roo.each(this.tickItems, function(o){
17077             _this.addItem(o);
17078         });
17079         
17080         this.collapse();
17081         
17082     },
17083     
17084     validate : function()
17085     {
17086         if(this.getVisibilityEl().hasClass('hidden')){
17087             return true;
17088         }
17089         
17090         var v = this.getRawValue();
17091         
17092         if(this.multiple){
17093             v = this.getValue();
17094         }
17095         
17096         if(this.disabled || this.allowBlank || v.length){
17097             this.markValid();
17098             return true;
17099         }
17100         
17101         this.markInvalid();
17102         return false;
17103     },
17104     
17105     tickableInputEl : function()
17106     {
17107         if(!this.tickable || !this.editable){
17108             return this.inputEl();
17109         }
17110         
17111         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17112     },
17113     
17114     
17115     getAutoCreateTouchView : function()
17116     {
17117         var id = Roo.id();
17118         
17119         var cfg = {
17120             cls: 'form-group' //input-group
17121         };
17122         
17123         var input =  {
17124             tag: 'input',
17125             id : id,
17126             type : this.inputType,
17127             cls : 'form-control x-combo-noedit',
17128             autocomplete: 'new-password',
17129             placeholder : this.placeholder || '',
17130             readonly : true
17131         };
17132         
17133         if (this.name) {
17134             input.name = this.name;
17135         }
17136         
17137         if (this.size) {
17138             input.cls += ' input-' + this.size;
17139         }
17140         
17141         if (this.disabled) {
17142             input.disabled = true;
17143         }
17144         
17145         var inputblock = {
17146             cls : 'roo-combobox-wrap',
17147             cn : [
17148                 input
17149             ]
17150         };
17151         
17152         if(this.before){
17153             inputblock.cls += ' input-group';
17154             
17155             inputblock.cn.unshift({
17156                 tag :'span',
17157                 cls : 'input-group-addon input-group-prepend input-group-text',
17158                 html : this.before
17159             });
17160         }
17161         
17162         if(this.removable && !this.multiple){
17163             inputblock.cls += ' roo-removable';
17164             
17165             inputblock.cn.push({
17166                 tag: 'button',
17167                 html : 'x',
17168                 cls : 'roo-combo-removable-btn close'
17169             });
17170         }
17171
17172         if(this.hasFeedback && !this.allowBlank){
17173             
17174             inputblock.cls += ' has-feedback';
17175             
17176             inputblock.cn.push({
17177                 tag: 'span',
17178                 cls: 'glyphicon form-control-feedback'
17179             });
17180             
17181         }
17182         
17183         if (this.after) {
17184             
17185             inputblock.cls += (this.before) ? '' : ' input-group';
17186             
17187             inputblock.cn.push({
17188                 tag :'span',
17189                 cls : 'input-group-addon input-group-append input-group-text',
17190                 html : this.after
17191             });
17192         }
17193
17194         
17195         var ibwrap = inputblock;
17196         
17197         if(this.multiple){
17198             ibwrap = {
17199                 tag: 'ul',
17200                 cls: 'roo-select2-choices',
17201                 cn:[
17202                     {
17203                         tag: 'li',
17204                         cls: 'roo-select2-search-field',
17205                         cn: [
17206
17207                             inputblock
17208                         ]
17209                     }
17210                 ]
17211             };
17212         
17213             
17214         }
17215         
17216         var combobox = {
17217             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17218             cn: [
17219                 {
17220                     tag: 'input',
17221                     type : 'hidden',
17222                     cls: 'form-hidden-field'
17223                 },
17224                 ibwrap
17225             ]
17226         };
17227         
17228         if(!this.multiple && this.showToggleBtn){
17229             
17230             var caret = {
17231                 cls: 'caret'
17232             };
17233             
17234             if (this.caret != false) {
17235                 caret = {
17236                      tag: 'i',
17237                      cls: 'fa fa-' + this.caret
17238                 };
17239                 
17240             }
17241             
17242             combobox.cn.push({
17243                 tag :'span',
17244                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17245                 cn : [
17246                     Roo.bootstrap.version == 3 ? caret : '',
17247                     {
17248                         tag: 'span',
17249                         cls: 'combobox-clear',
17250                         cn  : [
17251                             {
17252                                 tag : 'i',
17253                                 cls: 'icon-remove'
17254                             }
17255                         ]
17256                     }
17257                 ]
17258
17259             })
17260         }
17261         
17262         if(this.multiple){
17263             combobox.cls += ' roo-select2-container-multi';
17264         }
17265         
17266         var align = this.labelAlign || this.parentLabelAlign();
17267         
17268         if (align ==='left' && this.fieldLabel.length) {
17269
17270             cfg.cn = [
17271                 {
17272                    tag : 'i',
17273                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17274                    tooltip : 'This field is required'
17275                 },
17276                 {
17277                     tag: 'label',
17278                     cls : 'control-label col-form-label',
17279                     html : this.fieldLabel
17280
17281                 },
17282                 {
17283                     cls : 'roo-combobox-wrap ', 
17284                     cn: [
17285                         combobox
17286                     ]
17287                 }
17288             ];
17289             
17290             var labelCfg = cfg.cn[1];
17291             var contentCfg = cfg.cn[2];
17292             
17293
17294             if(this.indicatorpos == 'right'){
17295                 cfg.cn = [
17296                     {
17297                         tag: 'label',
17298                         'for' :  id,
17299                         cls : 'control-label col-form-label',
17300                         cn : [
17301                             {
17302                                 tag : 'span',
17303                                 html : this.fieldLabel
17304                             },
17305                             {
17306                                 tag : 'i',
17307                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17308                                 tooltip : 'This field is required'
17309                             }
17310                         ]
17311                     },
17312                     {
17313                         cls : "roo-combobox-wrap ",
17314                         cn: [
17315                             combobox
17316                         ]
17317                     }
17318
17319                 ];
17320                 
17321                 labelCfg = cfg.cn[0];
17322                 contentCfg = cfg.cn[1];
17323             }
17324             
17325            
17326             
17327             if(this.labelWidth > 12){
17328                 labelCfg.style = "width: " + this.labelWidth + 'px';
17329             }
17330            
17331             if(this.labelWidth < 13 && this.labelmd == 0){
17332                 this.labelmd = this.labelWidth;
17333             }
17334             
17335             if(this.labellg > 0){
17336                 labelCfg.cls += ' col-lg-' + this.labellg;
17337                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17338             }
17339             
17340             if(this.labelmd > 0){
17341                 labelCfg.cls += ' col-md-' + this.labelmd;
17342                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17343             }
17344             
17345             if(this.labelsm > 0){
17346                 labelCfg.cls += ' col-sm-' + this.labelsm;
17347                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17348             }
17349             
17350             if(this.labelxs > 0){
17351                 labelCfg.cls += ' col-xs-' + this.labelxs;
17352                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17353             }
17354                 
17355                 
17356         } else if ( this.fieldLabel.length) {
17357             cfg.cn = [
17358                 {
17359                    tag : 'i',
17360                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17361                    tooltip : 'This field is required'
17362                 },
17363                 {
17364                     tag: 'label',
17365                     cls : 'control-label',
17366                     html : this.fieldLabel
17367
17368                 },
17369                 {
17370                     cls : '', 
17371                     cn: [
17372                         combobox
17373                     ]
17374                 }
17375             ];
17376             
17377             if(this.indicatorpos == 'right'){
17378                 cfg.cn = [
17379                     {
17380                         tag: 'label',
17381                         cls : 'control-label',
17382                         html : this.fieldLabel,
17383                         cn : [
17384                             {
17385                                tag : 'i',
17386                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17387                                tooltip : 'This field is required'
17388                             }
17389                         ]
17390                     },
17391                     {
17392                         cls : '', 
17393                         cn: [
17394                             combobox
17395                         ]
17396                     }
17397                 ];
17398             }
17399         } else {
17400             cfg.cn = combobox;    
17401         }
17402         
17403         
17404         var settings = this;
17405         
17406         ['xs','sm','md','lg'].map(function(size){
17407             if (settings[size]) {
17408                 cfg.cls += ' col-' + size + '-' + settings[size];
17409             }
17410         });
17411         
17412         return cfg;
17413     },
17414     
17415     initTouchView : function()
17416     {
17417         this.renderTouchView();
17418         
17419         this.touchViewEl.on('scroll', function(){
17420             this.el.dom.scrollTop = 0;
17421         }, this);
17422         
17423         this.originalValue = this.getValue();
17424         
17425         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17426         
17427         this.inputEl().on("click", this.showTouchView, this);
17428         if (this.triggerEl) {
17429             this.triggerEl.on("click", this.showTouchView, this);
17430         }
17431         
17432         
17433         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17434         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17435         
17436         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17437         
17438         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17439         this.store.on('load', this.onTouchViewLoad, this);
17440         this.store.on('loadexception', this.onTouchViewLoadException, this);
17441         
17442         if(this.hiddenName){
17443             
17444             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17445             
17446             this.hiddenField.dom.value =
17447                 this.hiddenValue !== undefined ? this.hiddenValue :
17448                 this.value !== undefined ? this.value : '';
17449         
17450             this.el.dom.removeAttribute('name');
17451             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17452         }
17453         
17454         if(this.multiple){
17455             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17456             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17457         }
17458         
17459         if(this.removable && !this.multiple){
17460             var close = this.closeTriggerEl();
17461             if(close){
17462                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17463                 close.on('click', this.removeBtnClick, this, close);
17464             }
17465         }
17466         /*
17467          * fix the bug in Safari iOS8
17468          */
17469         this.inputEl().on("focus", function(e){
17470             document.activeElement.blur();
17471         }, this);
17472         
17473         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17474         
17475         return;
17476         
17477         
17478     },
17479     
17480     renderTouchView : function()
17481     {
17482         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17483         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17484         
17485         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17486         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17487         
17488         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17489         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17490         this.touchViewBodyEl.setStyle('overflow', 'auto');
17491         
17492         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17493         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17494         
17495         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17496         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17497         
17498     },
17499     
17500     showTouchView : function()
17501     {
17502         if(this.disabled){
17503             return;
17504         }
17505         
17506         this.touchViewHeaderEl.hide();
17507
17508         if(this.modalTitle.length){
17509             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17510             this.touchViewHeaderEl.show();
17511         }
17512
17513         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17514         this.touchViewEl.show();
17515
17516         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17517         
17518         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17519         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17520
17521         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17522
17523         if(this.modalTitle.length){
17524             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17525         }
17526         
17527         this.touchViewBodyEl.setHeight(bodyHeight);
17528
17529         if(this.animate){
17530             var _this = this;
17531             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17532         }else{
17533             this.touchViewEl.addClass(['in','show']);
17534         }
17535         
17536         if(this._touchViewMask){
17537             Roo.get(document.body).addClass("x-body-masked");
17538             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17539             this._touchViewMask.setStyle('z-index', 10000);
17540             this._touchViewMask.addClass('show');
17541         }
17542         
17543         this.doTouchViewQuery();
17544         
17545     },
17546     
17547     hideTouchView : function()
17548     {
17549         this.touchViewEl.removeClass(['in','show']);
17550
17551         if(this.animate){
17552             var _this = this;
17553             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17554         }else{
17555             this.touchViewEl.setStyle('display', 'none');
17556         }
17557         
17558         if(this._touchViewMask){
17559             this._touchViewMask.removeClass('show');
17560             Roo.get(document.body).removeClass("x-body-masked");
17561         }
17562     },
17563     
17564     setTouchViewValue : function()
17565     {
17566         if(this.multiple){
17567             this.clearItem();
17568         
17569             var _this = this;
17570
17571             Roo.each(this.tickItems, function(o){
17572                 this.addItem(o);
17573             }, this);
17574         }
17575         
17576         this.hideTouchView();
17577     },
17578     
17579     doTouchViewQuery : function()
17580     {
17581         var qe = {
17582             query: '',
17583             forceAll: true,
17584             combo: this,
17585             cancel:false
17586         };
17587         
17588         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17589             return false;
17590         }
17591         
17592         if(!this.alwaysQuery || this.mode == 'local'){
17593             this.onTouchViewLoad();
17594             return;
17595         }
17596         
17597         this.store.load();
17598     },
17599     
17600     onTouchViewBeforeLoad : function(combo,opts)
17601     {
17602         return;
17603     },
17604
17605     // private
17606     onTouchViewLoad : function()
17607     {
17608         if(this.store.getCount() < 1){
17609             this.onTouchViewEmptyResults();
17610             return;
17611         }
17612         
17613         this.clearTouchView();
17614         
17615         var rawValue = this.getRawValue();
17616         
17617         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17618         
17619         this.tickItems = [];
17620         
17621         this.store.data.each(function(d, rowIndex){
17622             var row = this.touchViewListGroup.createChild(template);
17623             
17624             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17625                 row.addClass(d.data.cls);
17626             }
17627             
17628             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17629                 var cfg = {
17630                     data : d.data,
17631                     html : d.data[this.displayField]
17632                 };
17633                 
17634                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17635                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17636                 }
17637             }
17638             row.removeClass('selected');
17639             if(!this.multiple && this.valueField &&
17640                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17641             {
17642                 // radio buttons..
17643                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17644                 row.addClass('selected');
17645             }
17646             
17647             if(this.multiple && this.valueField &&
17648                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17649             {
17650                 
17651                 // checkboxes...
17652                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17653                 this.tickItems.push(d.data);
17654             }
17655             
17656             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17657             
17658         }, this);
17659         
17660         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17661         
17662         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17663
17664         if(this.modalTitle.length){
17665             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17666         }
17667
17668         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17669         
17670         if(this.mobile_restrict_height && listHeight < bodyHeight){
17671             this.touchViewBodyEl.setHeight(listHeight);
17672         }
17673         
17674         var _this = this;
17675         
17676         if(firstChecked && listHeight > bodyHeight){
17677             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17678         }
17679         
17680     },
17681     
17682     onTouchViewLoadException : function()
17683     {
17684         this.hideTouchView();
17685     },
17686     
17687     onTouchViewEmptyResults : function()
17688     {
17689         this.clearTouchView();
17690         
17691         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17692         
17693         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17694         
17695     },
17696     
17697     clearTouchView : function()
17698     {
17699         this.touchViewListGroup.dom.innerHTML = '';
17700     },
17701     
17702     onTouchViewClick : function(e, el, o)
17703     {
17704         e.preventDefault();
17705         
17706         var row = o.row;
17707         var rowIndex = o.rowIndex;
17708         
17709         var r = this.store.getAt(rowIndex);
17710         
17711         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17712             
17713             if(!this.multiple){
17714                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17715                     c.dom.removeAttribute('checked');
17716                 }, this);
17717
17718                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17719
17720                 this.setFromData(r.data);
17721
17722                 var close = this.closeTriggerEl();
17723
17724                 if(close){
17725                     close.show();
17726                 }
17727
17728                 this.hideTouchView();
17729
17730                 this.fireEvent('select', this, r, rowIndex);
17731
17732                 return;
17733             }
17734
17735             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17736                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17737                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17738                 return;
17739             }
17740
17741             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17742             this.addItem(r.data);
17743             this.tickItems.push(r.data);
17744         }
17745     },
17746     
17747     getAutoCreateNativeIOS : function()
17748     {
17749         var cfg = {
17750             cls: 'form-group' //input-group,
17751         };
17752         
17753         var combobox =  {
17754             tag: 'select',
17755             cls : 'roo-ios-select'
17756         };
17757         
17758         if (this.name) {
17759             combobox.name = this.name;
17760         }
17761         
17762         if (this.disabled) {
17763             combobox.disabled = true;
17764         }
17765         
17766         var settings = this;
17767         
17768         ['xs','sm','md','lg'].map(function(size){
17769             if (settings[size]) {
17770                 cfg.cls += ' col-' + size + '-' + settings[size];
17771             }
17772         });
17773         
17774         cfg.cn = combobox;
17775         
17776         return cfg;
17777         
17778     },
17779     
17780     initIOSView : function()
17781     {
17782         this.store.on('load', this.onIOSViewLoad, this);
17783         
17784         return;
17785     },
17786     
17787     onIOSViewLoad : function()
17788     {
17789         if(this.store.getCount() < 1){
17790             return;
17791         }
17792         
17793         this.clearIOSView();
17794         
17795         if(this.allowBlank) {
17796             
17797             var default_text = '-- SELECT --';
17798             
17799             if(this.placeholder.length){
17800                 default_text = this.placeholder;
17801             }
17802             
17803             if(this.emptyTitle.length){
17804                 default_text += ' - ' + this.emptyTitle + ' -';
17805             }
17806             
17807             var opt = this.inputEl().createChild({
17808                 tag: 'option',
17809                 value : 0,
17810                 html : default_text
17811             });
17812             
17813             var o = {};
17814             o[this.valueField] = 0;
17815             o[this.displayField] = default_text;
17816             
17817             this.ios_options.push({
17818                 data : o,
17819                 el : opt
17820             });
17821             
17822         }
17823         
17824         this.store.data.each(function(d, rowIndex){
17825             
17826             var html = '';
17827             
17828             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17829                 html = d.data[this.displayField];
17830             }
17831             
17832             var value = '';
17833             
17834             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17835                 value = d.data[this.valueField];
17836             }
17837             
17838             var option = {
17839                 tag: 'option',
17840                 value : value,
17841                 html : html
17842             };
17843             
17844             if(this.value == d.data[this.valueField]){
17845                 option['selected'] = true;
17846             }
17847             
17848             var opt = this.inputEl().createChild(option);
17849             
17850             this.ios_options.push({
17851                 data : d.data,
17852                 el : opt
17853             });
17854             
17855         }, this);
17856         
17857         this.inputEl().on('change', function(){
17858            this.fireEvent('select', this);
17859         }, this);
17860         
17861     },
17862     
17863     clearIOSView: function()
17864     {
17865         this.inputEl().dom.innerHTML = '';
17866         
17867         this.ios_options = [];
17868     },
17869     
17870     setIOSValue: function(v)
17871     {
17872         this.value = v;
17873         
17874         if(!this.ios_options){
17875             return;
17876         }
17877         
17878         Roo.each(this.ios_options, function(opts){
17879            
17880            opts.el.dom.removeAttribute('selected');
17881            
17882            if(opts.data[this.valueField] != v){
17883                return;
17884            }
17885            
17886            opts.el.dom.setAttribute('selected', true);
17887            
17888         }, this);
17889     }
17890
17891     /** 
17892     * @cfg {Boolean} grow 
17893     * @hide 
17894     */
17895     /** 
17896     * @cfg {Number} growMin 
17897     * @hide 
17898     */
17899     /** 
17900     * @cfg {Number} growMax 
17901     * @hide 
17902     */
17903     /**
17904      * @hide
17905      * @method autoSize
17906      */
17907 });
17908
17909 Roo.apply(Roo.bootstrap.ComboBox,  {
17910     
17911     header : {
17912         tag: 'div',
17913         cls: 'modal-header',
17914         cn: [
17915             {
17916                 tag: 'h4',
17917                 cls: 'modal-title'
17918             }
17919         ]
17920     },
17921     
17922     body : {
17923         tag: 'div',
17924         cls: 'modal-body',
17925         cn: [
17926             {
17927                 tag: 'ul',
17928                 cls: 'list-group'
17929             }
17930         ]
17931     },
17932     
17933     listItemRadio : {
17934         tag: 'li',
17935         cls: 'list-group-item',
17936         cn: [
17937             {
17938                 tag: 'span',
17939                 cls: 'roo-combobox-list-group-item-value'
17940             },
17941             {
17942                 tag: 'div',
17943                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17944                 cn: [
17945                     {
17946                         tag: 'input',
17947                         type: 'radio'
17948                     },
17949                     {
17950                         tag: 'label'
17951                     }
17952                 ]
17953             }
17954         ]
17955     },
17956     
17957     listItemCheckbox : {
17958         tag: 'li',
17959         cls: 'list-group-item',
17960         cn: [
17961             {
17962                 tag: 'span',
17963                 cls: 'roo-combobox-list-group-item-value'
17964             },
17965             {
17966                 tag: 'div',
17967                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17968                 cn: [
17969                     {
17970                         tag: 'input',
17971                         type: 'checkbox'
17972                     },
17973                     {
17974                         tag: 'label'
17975                     }
17976                 ]
17977             }
17978         ]
17979     },
17980     
17981     emptyResult : {
17982         tag: 'div',
17983         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17984     },
17985     
17986     footer : {
17987         tag: 'div',
17988         cls: 'modal-footer',
17989         cn: [
17990             {
17991                 tag: 'div',
17992                 cls: 'row',
17993                 cn: [
17994                     {
17995                         tag: 'div',
17996                         cls: 'col-xs-6 text-left',
17997                         cn: {
17998                             tag: 'button',
17999                             cls: 'btn btn-danger roo-touch-view-cancel',
18000                             html: 'Cancel'
18001                         }
18002                     },
18003                     {
18004                         tag: 'div',
18005                         cls: 'col-xs-6 text-right',
18006                         cn: {
18007                             tag: 'button',
18008                             cls: 'btn btn-success roo-touch-view-ok',
18009                             html: 'OK'
18010                         }
18011                     }
18012                 ]
18013             }
18014         ]
18015         
18016     }
18017 });
18018
18019 Roo.apply(Roo.bootstrap.ComboBox,  {
18020     
18021     touchViewTemplate : {
18022         tag: 'div',
18023         cls: 'modal fade roo-combobox-touch-view',
18024         cn: [
18025             {
18026                 tag: 'div',
18027                 cls: 'modal-dialog',
18028                 style : 'position:fixed', // we have to fix position....
18029                 cn: [
18030                     {
18031                         tag: 'div',
18032                         cls: 'modal-content',
18033                         cn: [
18034                             Roo.bootstrap.ComboBox.header,
18035                             Roo.bootstrap.ComboBox.body,
18036                             Roo.bootstrap.ComboBox.footer
18037                         ]
18038                     }
18039                 ]
18040             }
18041         ]
18042     }
18043 });/*
18044  * Based on:
18045  * Ext JS Library 1.1.1
18046  * Copyright(c) 2006-2007, Ext JS, LLC.
18047  *
18048  * Originally Released Under LGPL - original licence link has changed is not relivant.
18049  *
18050  * Fork - LGPL
18051  * <script type="text/javascript">
18052  */
18053
18054 /**
18055  * @class Roo.View
18056  * @extends Roo.util.Observable
18057  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18058  * This class also supports single and multi selection modes. <br>
18059  * Create a data model bound view:
18060  <pre><code>
18061  var store = new Roo.data.Store(...);
18062
18063  var view = new Roo.View({
18064     el : "my-element",
18065     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18066  
18067     singleSelect: true,
18068     selectedClass: "ydataview-selected",
18069     store: store
18070  });
18071
18072  // listen for node click?
18073  view.on("click", function(vw, index, node, e){
18074  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18075  });
18076
18077  // load XML data
18078  dataModel.load("foobar.xml");
18079  </code></pre>
18080  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18081  * <br><br>
18082  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18083  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18084  * 
18085  * Note: old style constructor is still suported (container, template, config)
18086  * 
18087  * @constructor
18088  * Create a new View
18089  * @param {Object} config The config object
18090  * 
18091  */
18092 Roo.View = function(config, depreciated_tpl, depreciated_config){
18093     
18094     this.parent = false;
18095     
18096     if (typeof(depreciated_tpl) == 'undefined') {
18097         // new way.. - universal constructor.
18098         Roo.apply(this, config);
18099         this.el  = Roo.get(this.el);
18100     } else {
18101         // old format..
18102         this.el  = Roo.get(config);
18103         this.tpl = depreciated_tpl;
18104         Roo.apply(this, depreciated_config);
18105     }
18106     this.wrapEl  = this.el.wrap().wrap();
18107     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18108     
18109     
18110     if(typeof(this.tpl) == "string"){
18111         this.tpl = new Roo.Template(this.tpl);
18112     } else {
18113         // support xtype ctors..
18114         this.tpl = new Roo.factory(this.tpl, Roo);
18115     }
18116     
18117     
18118     this.tpl.compile();
18119     
18120     /** @private */
18121     this.addEvents({
18122         /**
18123          * @event beforeclick
18124          * Fires before a click is processed. Returns false to cancel the default action.
18125          * @param {Roo.View} this
18126          * @param {Number} index The index of the target node
18127          * @param {HTMLElement} node The target node
18128          * @param {Roo.EventObject} e The raw event object
18129          */
18130             "beforeclick" : true,
18131         /**
18132          * @event click
18133          * Fires when a template node is clicked.
18134          * @param {Roo.View} this
18135          * @param {Number} index The index of the target node
18136          * @param {HTMLElement} node The target node
18137          * @param {Roo.EventObject} e The raw event object
18138          */
18139             "click" : true,
18140         /**
18141          * @event dblclick
18142          * Fires when a template node is double clicked.
18143          * @param {Roo.View} this
18144          * @param {Number} index The index of the target node
18145          * @param {HTMLElement} node The target node
18146          * @param {Roo.EventObject} e The raw event object
18147          */
18148             "dblclick" : true,
18149         /**
18150          * @event contextmenu
18151          * Fires when a template node is right clicked.
18152          * @param {Roo.View} this
18153          * @param {Number} index The index of the target node
18154          * @param {HTMLElement} node The target node
18155          * @param {Roo.EventObject} e The raw event object
18156          */
18157             "contextmenu" : true,
18158         /**
18159          * @event selectionchange
18160          * Fires when the selected nodes change.
18161          * @param {Roo.View} this
18162          * @param {Array} selections Array of the selected nodes
18163          */
18164             "selectionchange" : true,
18165     
18166         /**
18167          * @event beforeselect
18168          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18169          * @param {Roo.View} this
18170          * @param {HTMLElement} node The node to be selected
18171          * @param {Array} selections Array of currently selected nodes
18172          */
18173             "beforeselect" : true,
18174         /**
18175          * @event preparedata
18176          * Fires on every row to render, to allow you to change the data.
18177          * @param {Roo.View} this
18178          * @param {Object} data to be rendered (change this)
18179          */
18180           "preparedata" : true
18181           
18182           
18183         });
18184
18185
18186
18187     this.el.on({
18188         "click": this.onClick,
18189         "dblclick": this.onDblClick,
18190         "contextmenu": this.onContextMenu,
18191         scope:this
18192     });
18193
18194     this.selections = [];
18195     this.nodes = [];
18196     this.cmp = new Roo.CompositeElementLite([]);
18197     if(this.store){
18198         this.store = Roo.factory(this.store, Roo.data);
18199         this.setStore(this.store, true);
18200     }
18201     
18202     if ( this.footer && this.footer.xtype) {
18203            
18204          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18205         
18206         this.footer.dataSource = this.store;
18207         this.footer.container = fctr;
18208         this.footer = Roo.factory(this.footer, Roo);
18209         fctr.insertFirst(this.el);
18210         
18211         // this is a bit insane - as the paging toolbar seems to detach the el..
18212 //        dom.parentNode.parentNode.parentNode
18213          // they get detached?
18214     }
18215     
18216     
18217     Roo.View.superclass.constructor.call(this);
18218     
18219     
18220 };
18221
18222 Roo.extend(Roo.View, Roo.util.Observable, {
18223     
18224      /**
18225      * @cfg {Roo.data.Store} store Data store to load data from.
18226      */
18227     store : false,
18228     
18229     /**
18230      * @cfg {String|Roo.Element} el The container element.
18231      */
18232     el : '',
18233     
18234     /**
18235      * @cfg {String|Roo.Template} tpl The template used by this View 
18236      */
18237     tpl : false,
18238     /**
18239      * @cfg {String} dataName the named area of the template to use as the data area
18240      *                          Works with domtemplates roo-name="name"
18241      */
18242     dataName: false,
18243     /**
18244      * @cfg {String} selectedClass The css class to add to selected nodes
18245      */
18246     selectedClass : "x-view-selected",
18247      /**
18248      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18249      */
18250     emptyText : "",
18251     
18252     /**
18253      * @cfg {String} text to display on mask (default Loading)
18254      */
18255     mask : false,
18256     /**
18257      * @cfg {Boolean} multiSelect Allow multiple selection
18258      */
18259     multiSelect : false,
18260     /**
18261      * @cfg {Boolean} singleSelect Allow single selection
18262      */
18263     singleSelect:  false,
18264     
18265     /**
18266      * @cfg {Boolean} toggleSelect - selecting 
18267      */
18268     toggleSelect : false,
18269     
18270     /**
18271      * @cfg {Boolean} tickable - selecting 
18272      */
18273     tickable : false,
18274     
18275     /**
18276      * Returns the element this view is bound to.
18277      * @return {Roo.Element}
18278      */
18279     getEl : function(){
18280         return this.wrapEl;
18281     },
18282     
18283     
18284
18285     /**
18286      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18287      */
18288     refresh : function(){
18289         //Roo.log('refresh');
18290         var t = this.tpl;
18291         
18292         // if we are using something like 'domtemplate', then
18293         // the what gets used is:
18294         // t.applySubtemplate(NAME, data, wrapping data..)
18295         // the outer template then get' applied with
18296         //     the store 'extra data'
18297         // and the body get's added to the
18298         //      roo-name="data" node?
18299         //      <span class='roo-tpl-{name}'></span> ?????
18300         
18301         
18302         
18303         this.clearSelections();
18304         this.el.update("");
18305         var html = [];
18306         var records = this.store.getRange();
18307         if(records.length < 1) {
18308             
18309             // is this valid??  = should it render a template??
18310             
18311             this.el.update(this.emptyText);
18312             return;
18313         }
18314         var el = this.el;
18315         if (this.dataName) {
18316             this.el.update(t.apply(this.store.meta)); //????
18317             el = this.el.child('.roo-tpl-' + this.dataName);
18318         }
18319         
18320         for(var i = 0, len = records.length; i < len; i++){
18321             var data = this.prepareData(records[i].data, i, records[i]);
18322             this.fireEvent("preparedata", this, data, i, records[i]);
18323             
18324             var d = Roo.apply({}, data);
18325             
18326             if(this.tickable){
18327                 Roo.apply(d, {'roo-id' : Roo.id()});
18328                 
18329                 var _this = this;
18330             
18331                 Roo.each(this.parent.item, function(item){
18332                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18333                         return;
18334                     }
18335                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18336                 });
18337             }
18338             
18339             html[html.length] = Roo.util.Format.trim(
18340                 this.dataName ?
18341                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18342                     t.apply(d)
18343             );
18344         }
18345         
18346         
18347         
18348         el.update(html.join(""));
18349         this.nodes = el.dom.childNodes;
18350         this.updateIndexes(0);
18351     },
18352     
18353
18354     /**
18355      * Function to override to reformat the data that is sent to
18356      * the template for each node.
18357      * DEPRICATED - use the preparedata event handler.
18358      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18359      * a JSON object for an UpdateManager bound view).
18360      */
18361     prepareData : function(data, index, record)
18362     {
18363         this.fireEvent("preparedata", this, data, index, record);
18364         return data;
18365     },
18366
18367     onUpdate : function(ds, record){
18368         // Roo.log('on update');   
18369         this.clearSelections();
18370         var index = this.store.indexOf(record);
18371         var n = this.nodes[index];
18372         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18373         n.parentNode.removeChild(n);
18374         this.updateIndexes(index, index);
18375     },
18376
18377     
18378     
18379 // --------- FIXME     
18380     onAdd : function(ds, records, index)
18381     {
18382         //Roo.log(['on Add', ds, records, index] );        
18383         this.clearSelections();
18384         if(this.nodes.length == 0){
18385             this.refresh();
18386             return;
18387         }
18388         var n = this.nodes[index];
18389         for(var i = 0, len = records.length; i < len; i++){
18390             var d = this.prepareData(records[i].data, i, records[i]);
18391             if(n){
18392                 this.tpl.insertBefore(n, d);
18393             }else{
18394                 
18395                 this.tpl.append(this.el, d);
18396             }
18397         }
18398         this.updateIndexes(index);
18399     },
18400
18401     onRemove : function(ds, record, index){
18402        // Roo.log('onRemove');
18403         this.clearSelections();
18404         var el = this.dataName  ?
18405             this.el.child('.roo-tpl-' + this.dataName) :
18406             this.el; 
18407         
18408         el.dom.removeChild(this.nodes[index]);
18409         this.updateIndexes(index);
18410     },
18411
18412     /**
18413      * Refresh an individual node.
18414      * @param {Number} index
18415      */
18416     refreshNode : function(index){
18417         this.onUpdate(this.store, this.store.getAt(index));
18418     },
18419
18420     updateIndexes : function(startIndex, endIndex){
18421         var ns = this.nodes;
18422         startIndex = startIndex || 0;
18423         endIndex = endIndex || ns.length - 1;
18424         for(var i = startIndex; i <= endIndex; i++){
18425             ns[i].nodeIndex = i;
18426         }
18427     },
18428
18429     /**
18430      * Changes the data store this view uses and refresh the view.
18431      * @param {Store} store
18432      */
18433     setStore : function(store, initial){
18434         if(!initial && this.store){
18435             this.store.un("datachanged", this.refresh);
18436             this.store.un("add", this.onAdd);
18437             this.store.un("remove", this.onRemove);
18438             this.store.un("update", this.onUpdate);
18439             this.store.un("clear", this.refresh);
18440             this.store.un("beforeload", this.onBeforeLoad);
18441             this.store.un("load", this.onLoad);
18442             this.store.un("loadexception", this.onLoad);
18443         }
18444         if(store){
18445           
18446             store.on("datachanged", this.refresh, this);
18447             store.on("add", this.onAdd, this);
18448             store.on("remove", this.onRemove, this);
18449             store.on("update", this.onUpdate, this);
18450             store.on("clear", this.refresh, this);
18451             store.on("beforeload", this.onBeforeLoad, this);
18452             store.on("load", this.onLoad, this);
18453             store.on("loadexception", this.onLoad, this);
18454         }
18455         
18456         if(store){
18457             this.refresh();
18458         }
18459     },
18460     /**
18461      * onbeforeLoad - masks the loading area.
18462      *
18463      */
18464     onBeforeLoad : function(store,opts)
18465     {
18466          //Roo.log('onBeforeLoad');   
18467         if (!opts.add) {
18468             this.el.update("");
18469         }
18470         this.el.mask(this.mask ? this.mask : "Loading" ); 
18471     },
18472     onLoad : function ()
18473     {
18474         this.el.unmask();
18475     },
18476     
18477
18478     /**
18479      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18480      * @param {HTMLElement} node
18481      * @return {HTMLElement} The template node
18482      */
18483     findItemFromChild : function(node){
18484         var el = this.dataName  ?
18485             this.el.child('.roo-tpl-' + this.dataName,true) :
18486             this.el.dom; 
18487         
18488         if(!node || node.parentNode == el){
18489                     return node;
18490             }
18491             var p = node.parentNode;
18492             while(p && p != el){
18493             if(p.parentNode == el){
18494                 return p;
18495             }
18496             p = p.parentNode;
18497         }
18498             return null;
18499     },
18500
18501     /** @ignore */
18502     onClick : function(e){
18503         var item = this.findItemFromChild(e.getTarget());
18504         if(item){
18505             var index = this.indexOf(item);
18506             if(this.onItemClick(item, index, e) !== false){
18507                 this.fireEvent("click", this, index, item, e);
18508             }
18509         }else{
18510             this.clearSelections();
18511         }
18512     },
18513
18514     /** @ignore */
18515     onContextMenu : function(e){
18516         var item = this.findItemFromChild(e.getTarget());
18517         if(item){
18518             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18519         }
18520     },
18521
18522     /** @ignore */
18523     onDblClick : function(e){
18524         var item = this.findItemFromChild(e.getTarget());
18525         if(item){
18526             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18527         }
18528     },
18529
18530     onItemClick : function(item, index, e)
18531     {
18532         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18533             return false;
18534         }
18535         if (this.toggleSelect) {
18536             var m = this.isSelected(item) ? 'unselect' : 'select';
18537             //Roo.log(m);
18538             var _t = this;
18539             _t[m](item, true, false);
18540             return true;
18541         }
18542         if(this.multiSelect || this.singleSelect){
18543             if(this.multiSelect && e.shiftKey && this.lastSelection){
18544                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18545             }else{
18546                 this.select(item, this.multiSelect && e.ctrlKey);
18547                 this.lastSelection = item;
18548             }
18549             
18550             if(!this.tickable){
18551                 e.preventDefault();
18552             }
18553             
18554         }
18555         return true;
18556     },
18557
18558     /**
18559      * Get the number of selected nodes.
18560      * @return {Number}
18561      */
18562     getSelectionCount : function(){
18563         return this.selections.length;
18564     },
18565
18566     /**
18567      * Get the currently selected nodes.
18568      * @return {Array} An array of HTMLElements
18569      */
18570     getSelectedNodes : function(){
18571         return this.selections;
18572     },
18573
18574     /**
18575      * Get the indexes of the selected nodes.
18576      * @return {Array}
18577      */
18578     getSelectedIndexes : function(){
18579         var indexes = [], s = this.selections;
18580         for(var i = 0, len = s.length; i < len; i++){
18581             indexes.push(s[i].nodeIndex);
18582         }
18583         return indexes;
18584     },
18585
18586     /**
18587      * Clear all selections
18588      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18589      */
18590     clearSelections : function(suppressEvent){
18591         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18592             this.cmp.elements = this.selections;
18593             this.cmp.removeClass(this.selectedClass);
18594             this.selections = [];
18595             if(!suppressEvent){
18596                 this.fireEvent("selectionchange", this, this.selections);
18597             }
18598         }
18599     },
18600
18601     /**
18602      * Returns true if the passed node is selected
18603      * @param {HTMLElement/Number} node The node or node index
18604      * @return {Boolean}
18605      */
18606     isSelected : function(node){
18607         var s = this.selections;
18608         if(s.length < 1){
18609             return false;
18610         }
18611         node = this.getNode(node);
18612         return s.indexOf(node) !== -1;
18613     },
18614
18615     /**
18616      * Selects nodes.
18617      * @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
18618      * @param {Boolean} keepExisting (optional) true to keep existing selections
18619      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18620      */
18621     select : function(nodeInfo, keepExisting, suppressEvent){
18622         if(nodeInfo instanceof Array){
18623             if(!keepExisting){
18624                 this.clearSelections(true);
18625             }
18626             for(var i = 0, len = nodeInfo.length; i < len; i++){
18627                 this.select(nodeInfo[i], true, true);
18628             }
18629             return;
18630         } 
18631         var node = this.getNode(nodeInfo);
18632         if(!node || this.isSelected(node)){
18633             return; // already selected.
18634         }
18635         if(!keepExisting){
18636             this.clearSelections(true);
18637         }
18638         
18639         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18640             Roo.fly(node).addClass(this.selectedClass);
18641             this.selections.push(node);
18642             if(!suppressEvent){
18643                 this.fireEvent("selectionchange", this, this.selections);
18644             }
18645         }
18646         
18647         
18648     },
18649       /**
18650      * Unselects nodes.
18651      * @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
18652      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18653      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18654      */
18655     unselect : function(nodeInfo, keepExisting, suppressEvent)
18656     {
18657         if(nodeInfo instanceof Array){
18658             Roo.each(this.selections, function(s) {
18659                 this.unselect(s, nodeInfo);
18660             }, this);
18661             return;
18662         }
18663         var node = this.getNode(nodeInfo);
18664         if(!node || !this.isSelected(node)){
18665             //Roo.log("not selected");
18666             return; // not selected.
18667         }
18668         // fireevent???
18669         var ns = [];
18670         Roo.each(this.selections, function(s) {
18671             if (s == node ) {
18672                 Roo.fly(node).removeClass(this.selectedClass);
18673
18674                 return;
18675             }
18676             ns.push(s);
18677         },this);
18678         
18679         this.selections= ns;
18680         this.fireEvent("selectionchange", this, this.selections);
18681     },
18682
18683     /**
18684      * Gets a template node.
18685      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18686      * @return {HTMLElement} The node or null if it wasn't found
18687      */
18688     getNode : function(nodeInfo){
18689         if(typeof nodeInfo == "string"){
18690             return document.getElementById(nodeInfo);
18691         }else if(typeof nodeInfo == "number"){
18692             return this.nodes[nodeInfo];
18693         }
18694         return nodeInfo;
18695     },
18696
18697     /**
18698      * Gets a range template nodes.
18699      * @param {Number} startIndex
18700      * @param {Number} endIndex
18701      * @return {Array} An array of nodes
18702      */
18703     getNodes : function(start, end){
18704         var ns = this.nodes;
18705         start = start || 0;
18706         end = typeof end == "undefined" ? ns.length - 1 : end;
18707         var nodes = [];
18708         if(start <= end){
18709             for(var i = start; i <= end; i++){
18710                 nodes.push(ns[i]);
18711             }
18712         } else{
18713             for(var i = start; i >= end; i--){
18714                 nodes.push(ns[i]);
18715             }
18716         }
18717         return nodes;
18718     },
18719
18720     /**
18721      * Finds the index of the passed node
18722      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18723      * @return {Number} The index of the node or -1
18724      */
18725     indexOf : function(node){
18726         node = this.getNode(node);
18727         if(typeof node.nodeIndex == "number"){
18728             return node.nodeIndex;
18729         }
18730         var ns = this.nodes;
18731         for(var i = 0, len = ns.length; i < len; i++){
18732             if(ns[i] == node){
18733                 return i;
18734             }
18735         }
18736         return -1;
18737     }
18738 });
18739 /*
18740  * - LGPL
18741  *
18742  * based on jquery fullcalendar
18743  * 
18744  */
18745
18746 Roo.bootstrap = Roo.bootstrap || {};
18747 /**
18748  * @class Roo.bootstrap.Calendar
18749  * @extends Roo.bootstrap.Component
18750  * Bootstrap Calendar class
18751  * @cfg {Boolean} loadMask (true|false) default false
18752  * @cfg {Object} header generate the user specific header of the calendar, default false
18753
18754  * @constructor
18755  * Create a new Container
18756  * @param {Object} config The config object
18757  */
18758
18759
18760
18761 Roo.bootstrap.Calendar = function(config){
18762     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18763      this.addEvents({
18764         /**
18765              * @event select
18766              * Fires when a date is selected
18767              * @param {DatePicker} this
18768              * @param {Date} date The selected date
18769              */
18770         'select': true,
18771         /**
18772              * @event monthchange
18773              * Fires when the displayed month changes 
18774              * @param {DatePicker} this
18775              * @param {Date} date The selected month
18776              */
18777         'monthchange': true,
18778         /**
18779              * @event evententer
18780              * Fires when mouse over an event
18781              * @param {Calendar} this
18782              * @param {event} Event
18783              */
18784         'evententer': true,
18785         /**
18786              * @event eventleave
18787              * Fires when the mouse leaves an
18788              * @param {Calendar} this
18789              * @param {event}
18790              */
18791         'eventleave': true,
18792         /**
18793              * @event eventclick
18794              * Fires when the mouse click an
18795              * @param {Calendar} this
18796              * @param {event}
18797              */
18798         'eventclick': true
18799         
18800     });
18801
18802 };
18803
18804 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18805     
18806      /**
18807      * @cfg {Number} startDay
18808      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18809      */
18810     startDay : 0,
18811     
18812     loadMask : false,
18813     
18814     header : false,
18815       
18816     getAutoCreate : function(){
18817         
18818         
18819         var fc_button = function(name, corner, style, content ) {
18820             return Roo.apply({},{
18821                 tag : 'span',
18822                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18823                          (corner.length ?
18824                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18825                             ''
18826                         ),
18827                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18828                 unselectable: 'on'
18829             });
18830         };
18831         
18832         var header = {};
18833         
18834         if(!this.header){
18835             header = {
18836                 tag : 'table',
18837                 cls : 'fc-header',
18838                 style : 'width:100%',
18839                 cn : [
18840                     {
18841                         tag: 'tr',
18842                         cn : [
18843                             {
18844                                 tag : 'td',
18845                                 cls : 'fc-header-left',
18846                                 cn : [
18847                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18848                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18849                                     { tag: 'span', cls: 'fc-header-space' },
18850                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18851
18852
18853                                 ]
18854                             },
18855
18856                             {
18857                                 tag : 'td',
18858                                 cls : 'fc-header-center',
18859                                 cn : [
18860                                     {
18861                                         tag: 'span',
18862                                         cls: 'fc-header-title',
18863                                         cn : {
18864                                             tag: 'H2',
18865                                             html : 'month / year'
18866                                         }
18867                                     }
18868
18869                                 ]
18870                             },
18871                             {
18872                                 tag : 'td',
18873                                 cls : 'fc-header-right',
18874                                 cn : [
18875                               /*      fc_button('month', 'left', '', 'month' ),
18876                                     fc_button('week', '', '', 'week' ),
18877                                     fc_button('day', 'right', '', 'day' )
18878                                 */    
18879
18880                                 ]
18881                             }
18882
18883                         ]
18884                     }
18885                 ]
18886             };
18887         }
18888         
18889         header = this.header;
18890         
18891        
18892         var cal_heads = function() {
18893             var ret = [];
18894             // fixme - handle this.
18895             
18896             for (var i =0; i < Date.dayNames.length; i++) {
18897                 var d = Date.dayNames[i];
18898                 ret.push({
18899                     tag: 'th',
18900                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18901                     html : d.substring(0,3)
18902                 });
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[6].cls += ' fc-last';
18907             return ret;
18908         };
18909         var cal_cell = function(n) {
18910             return  {
18911                 tag: 'td',
18912                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18913                 cn : [
18914                     {
18915                         cn : [
18916                             {
18917                                 cls: 'fc-day-number',
18918                                 html: 'D'
18919                             },
18920                             {
18921                                 cls: 'fc-day-content',
18922                              
18923                                 cn : [
18924                                      {
18925                                         style: 'position: relative;' // height: 17px;
18926                                     }
18927                                 ]
18928                             }
18929                             
18930                             
18931                         ]
18932                     }
18933                 ]
18934                 
18935             }
18936         };
18937         var cal_rows = function() {
18938             
18939             var ret = [];
18940             for (var r = 0; r < 6; r++) {
18941                 var row= {
18942                     tag : 'tr',
18943                     cls : 'fc-week',
18944                     cn : []
18945                 };
18946                 
18947                 for (var i =0; i < Date.dayNames.length; i++) {
18948                     var d = Date.dayNames[i];
18949                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18950
18951                 }
18952                 row.cn[0].cls+=' fc-first';
18953                 row.cn[0].cn[0].style = 'min-height:90px';
18954                 row.cn[6].cls+=' fc-last';
18955                 ret.push(row);
18956                 
18957             }
18958             ret[0].cls += ' fc-first';
18959             ret[4].cls += ' fc-prev-last';
18960             ret[5].cls += ' fc-last';
18961             return ret;
18962             
18963         };
18964         
18965         var cal_table = {
18966             tag: 'table',
18967             cls: 'fc-border-separate',
18968             style : 'width:100%',
18969             cellspacing  : 0,
18970             cn : [
18971                 { 
18972                     tag: 'thead',
18973                     cn : [
18974                         { 
18975                             tag: 'tr',
18976                             cls : 'fc-first fc-last',
18977                             cn : cal_heads()
18978                         }
18979                     ]
18980                 },
18981                 { 
18982                     tag: 'tbody',
18983                     cn : cal_rows()
18984                 }
18985                   
18986             ]
18987         };
18988          
18989          var cfg = {
18990             cls : 'fc fc-ltr',
18991             cn : [
18992                 header,
18993                 {
18994                     cls : 'fc-content',
18995                     style : "position: relative;",
18996                     cn : [
18997                         {
18998                             cls : 'fc-view fc-view-month fc-grid',
18999                             style : 'position: relative',
19000                             unselectable : 'on',
19001                             cn : [
19002                                 {
19003                                     cls : 'fc-event-container',
19004                                     style : 'position:absolute;z-index:8;top:0;left:0;'
19005                                 },
19006                                 cal_table
19007                             ]
19008                         }
19009                     ]
19010     
19011                 }
19012            ] 
19013             
19014         };
19015         
19016          
19017         
19018         return cfg;
19019     },
19020     
19021     
19022     initEvents : function()
19023     {
19024         if(!this.store){
19025             throw "can not find store for calendar";
19026         }
19027         
19028         var mark = {
19029             tag: "div",
19030             cls:"x-dlg-mask",
19031             style: "text-align:center",
19032             cn: [
19033                 {
19034                     tag: "div",
19035                     style: "background-color:white;width:50%;margin:250 auto",
19036                     cn: [
19037                         {
19038                             tag: "img",
19039                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19040                         },
19041                         {
19042                             tag: "span",
19043                             html: "Loading"
19044                         }
19045                         
19046                     ]
19047                 }
19048             ]
19049         };
19050         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19051         
19052         var size = this.el.select('.fc-content', true).first().getSize();
19053         this.maskEl.setSize(size.width, size.height);
19054         this.maskEl.enableDisplayMode("block");
19055         if(!this.loadMask){
19056             this.maskEl.hide();
19057         }
19058         
19059         this.store = Roo.factory(this.store, Roo.data);
19060         this.store.on('load', this.onLoad, this);
19061         this.store.on('beforeload', this.onBeforeLoad, this);
19062         
19063         this.resize();
19064         
19065         this.cells = this.el.select('.fc-day',true);
19066         //Roo.log(this.cells);
19067         this.textNodes = this.el.query('.fc-day-number');
19068         this.cells.addClassOnOver('fc-state-hover');
19069         
19070         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19071         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19072         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19073         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19074         
19075         this.on('monthchange', this.onMonthChange, this);
19076         
19077         this.update(new Date().clearTime());
19078     },
19079     
19080     resize : function() {
19081         var sz  = this.el.getSize();
19082         
19083         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19084         this.el.select('.fc-day-content div',true).setHeight(34);
19085     },
19086     
19087     
19088     // private
19089     showPrevMonth : function(e){
19090         this.update(this.activeDate.add("mo", -1));
19091     },
19092     showToday : function(e){
19093         this.update(new Date().clearTime());
19094     },
19095     // private
19096     showNextMonth : function(e){
19097         this.update(this.activeDate.add("mo", 1));
19098     },
19099
19100     // private
19101     showPrevYear : function(){
19102         this.update(this.activeDate.add("y", -1));
19103     },
19104
19105     // private
19106     showNextYear : function(){
19107         this.update(this.activeDate.add("y", 1));
19108     },
19109
19110     
19111    // private
19112     update : function(date)
19113     {
19114         var vd = this.activeDate;
19115         this.activeDate = date;
19116 //        if(vd && this.el){
19117 //            var t = date.getTime();
19118 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19119 //                Roo.log('using add remove');
19120 //                
19121 //                this.fireEvent('monthchange', this, date);
19122 //                
19123 //                this.cells.removeClass("fc-state-highlight");
19124 //                this.cells.each(function(c){
19125 //                   if(c.dateValue == t){
19126 //                       c.addClass("fc-state-highlight");
19127 //                       setTimeout(function(){
19128 //                            try{c.dom.firstChild.focus();}catch(e){}
19129 //                       }, 50);
19130 //                       return false;
19131 //                   }
19132 //                   return true;
19133 //                });
19134 //                return;
19135 //            }
19136 //        }
19137         
19138         var days = date.getDaysInMonth();
19139         
19140         var firstOfMonth = date.getFirstDateOfMonth();
19141         var startingPos = firstOfMonth.getDay()-this.startDay;
19142         
19143         if(startingPos < this.startDay){
19144             startingPos += 7;
19145         }
19146         
19147         var pm = date.add(Date.MONTH, -1);
19148         var prevStart = pm.getDaysInMonth()-startingPos;
19149 //        
19150         this.cells = this.el.select('.fc-day',true);
19151         this.textNodes = this.el.query('.fc-day-number');
19152         this.cells.addClassOnOver('fc-state-hover');
19153         
19154         var cells = this.cells.elements;
19155         var textEls = this.textNodes;
19156         
19157         Roo.each(cells, function(cell){
19158             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19159         });
19160         
19161         days += startingPos;
19162
19163         // convert everything to numbers so it's fast
19164         var day = 86400000;
19165         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19166         //Roo.log(d);
19167         //Roo.log(pm);
19168         //Roo.log(prevStart);
19169         
19170         var today = new Date().clearTime().getTime();
19171         var sel = date.clearTime().getTime();
19172         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19173         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19174         var ddMatch = this.disabledDatesRE;
19175         var ddText = this.disabledDatesText;
19176         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19177         var ddaysText = this.disabledDaysText;
19178         var format = this.format;
19179         
19180         var setCellClass = function(cal, cell){
19181             cell.row = 0;
19182             cell.events = [];
19183             cell.more = [];
19184             //Roo.log('set Cell Class');
19185             cell.title = "";
19186             var t = d.getTime();
19187             
19188             //Roo.log(d);
19189             
19190             cell.dateValue = t;
19191             if(t == today){
19192                 cell.className += " fc-today";
19193                 cell.className += " fc-state-highlight";
19194                 cell.title = cal.todayText;
19195             }
19196             if(t == sel){
19197                 // disable highlight in other month..
19198                 //cell.className += " fc-state-highlight";
19199                 
19200             }
19201             // disabling
19202             if(t < min) {
19203                 cell.className = " fc-state-disabled";
19204                 cell.title = cal.minText;
19205                 return;
19206             }
19207             if(t > max) {
19208                 cell.className = " fc-state-disabled";
19209                 cell.title = cal.maxText;
19210                 return;
19211             }
19212             if(ddays){
19213                 if(ddays.indexOf(d.getDay()) != -1){
19214                     cell.title = ddaysText;
19215                     cell.className = " fc-state-disabled";
19216                 }
19217             }
19218             if(ddMatch && format){
19219                 var fvalue = d.dateFormat(format);
19220                 if(ddMatch.test(fvalue)){
19221                     cell.title = ddText.replace("%0", fvalue);
19222                     cell.className = " fc-state-disabled";
19223                 }
19224             }
19225             
19226             if (!cell.initialClassName) {
19227                 cell.initialClassName = cell.dom.className;
19228             }
19229             
19230             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19231         };
19232
19233         var i = 0;
19234         
19235         for(; i < startingPos; i++) {
19236             textEls[i].innerHTML = (++prevStart);
19237             d.setDate(d.getDate()+1);
19238             
19239             cells[i].className = "fc-past fc-other-month";
19240             setCellClass(this, cells[i]);
19241         }
19242         
19243         var intDay = 0;
19244         
19245         for(; i < days; i++){
19246             intDay = i - startingPos + 1;
19247             textEls[i].innerHTML = (intDay);
19248             d.setDate(d.getDate()+1);
19249             
19250             cells[i].className = ''; // "x-date-active";
19251             setCellClass(this, cells[i]);
19252         }
19253         var extraDays = 0;
19254         
19255         for(; i < 42; i++) {
19256             textEls[i].innerHTML = (++extraDays);
19257             d.setDate(d.getDate()+1);
19258             
19259             cells[i].className = "fc-future fc-other-month";
19260             setCellClass(this, cells[i]);
19261         }
19262         
19263         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19264         
19265         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19266         
19267         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19268         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19269         
19270         if(totalRows != 6){
19271             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19272             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19273         }
19274         
19275         this.fireEvent('monthchange', this, date);
19276         
19277         
19278         /*
19279         if(!this.internalRender){
19280             var main = this.el.dom.firstChild;
19281             var w = main.offsetWidth;
19282             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19283             Roo.fly(main).setWidth(w);
19284             this.internalRender = true;
19285             // opera does not respect the auto grow header center column
19286             // then, after it gets a width opera refuses to recalculate
19287             // without a second pass
19288             if(Roo.isOpera && !this.secondPass){
19289                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19290                 this.secondPass = true;
19291                 this.update.defer(10, this, [date]);
19292             }
19293         }
19294         */
19295         
19296     },
19297     
19298     findCell : function(dt) {
19299         dt = dt.clearTime().getTime();
19300         var ret = false;
19301         this.cells.each(function(c){
19302             //Roo.log("check " +c.dateValue + '?=' + dt);
19303             if(c.dateValue == dt){
19304                 ret = c;
19305                 return false;
19306             }
19307             return true;
19308         });
19309         
19310         return ret;
19311     },
19312     
19313     findCells : function(ev) {
19314         var s = ev.start.clone().clearTime().getTime();
19315        // Roo.log(s);
19316         var e= ev.end.clone().clearTime().getTime();
19317        // Roo.log(e);
19318         var ret = [];
19319         this.cells.each(function(c){
19320              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19321             
19322             if(c.dateValue > e){
19323                 return ;
19324             }
19325             if(c.dateValue < s){
19326                 return ;
19327             }
19328             ret.push(c);
19329         });
19330         
19331         return ret;    
19332     },
19333     
19334 //    findBestRow: function(cells)
19335 //    {
19336 //        var ret = 0;
19337 //        
19338 //        for (var i =0 ; i < cells.length;i++) {
19339 //            ret  = Math.max(cells[i].rows || 0,ret);
19340 //        }
19341 //        return ret;
19342 //        
19343 //    },
19344     
19345     
19346     addItem : function(ev)
19347     {
19348         // look for vertical location slot in
19349         var cells = this.findCells(ev);
19350         
19351 //        ev.row = this.findBestRow(cells);
19352         
19353         // work out the location.
19354         
19355         var crow = false;
19356         var rows = [];
19357         for(var i =0; i < cells.length; i++) {
19358             
19359             cells[i].row = cells[0].row;
19360             
19361             if(i == 0){
19362                 cells[i].row = cells[i].row + 1;
19363             }
19364             
19365             if (!crow) {
19366                 crow = {
19367                     start : cells[i],
19368                     end :  cells[i]
19369                 };
19370                 continue;
19371             }
19372             if (crow.start.getY() == cells[i].getY()) {
19373                 // on same row.
19374                 crow.end = cells[i];
19375                 continue;
19376             }
19377             // different row.
19378             rows.push(crow);
19379             crow = {
19380                 start: cells[i],
19381                 end : cells[i]
19382             };
19383             
19384         }
19385         
19386         rows.push(crow);
19387         ev.els = [];
19388         ev.rows = rows;
19389         ev.cells = cells;
19390         
19391         cells[0].events.push(ev);
19392         
19393         this.calevents.push(ev);
19394     },
19395     
19396     clearEvents: function() {
19397         
19398         if(!this.calevents){
19399             return;
19400         }
19401         
19402         Roo.each(this.cells.elements, function(c){
19403             c.row = 0;
19404             c.events = [];
19405             c.more = [];
19406         });
19407         
19408         Roo.each(this.calevents, function(e) {
19409             Roo.each(e.els, function(el) {
19410                 el.un('mouseenter' ,this.onEventEnter, this);
19411                 el.un('mouseleave' ,this.onEventLeave, this);
19412                 el.remove();
19413             },this);
19414         },this);
19415         
19416         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19417             e.remove();
19418         });
19419         
19420     },
19421     
19422     renderEvents: function()
19423     {   
19424         var _this = this;
19425         
19426         this.cells.each(function(c) {
19427             
19428             if(c.row < 5){
19429                 return;
19430             }
19431             
19432             var ev = c.events;
19433             
19434             var r = 4;
19435             if(c.row != c.events.length){
19436                 r = 4 - (4 - (c.row - c.events.length));
19437             }
19438             
19439             c.events = ev.slice(0, r);
19440             c.more = ev.slice(r);
19441             
19442             if(c.more.length && c.more.length == 1){
19443                 c.events.push(c.more.pop());
19444             }
19445             
19446             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19447             
19448         });
19449             
19450         this.cells.each(function(c) {
19451             
19452             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19453             
19454             
19455             for (var e = 0; e < c.events.length; e++){
19456                 var ev = c.events[e];
19457                 var rows = ev.rows;
19458                 
19459                 for(var i = 0; i < rows.length; i++) {
19460                 
19461                     // how many rows should it span..
19462
19463                     var  cfg = {
19464                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19465                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19466
19467                         unselectable : "on",
19468                         cn : [
19469                             {
19470                                 cls: 'fc-event-inner',
19471                                 cn : [
19472     //                                {
19473     //                                  tag:'span',
19474     //                                  cls: 'fc-event-time',
19475     //                                  html : cells.length > 1 ? '' : ev.time
19476     //                                },
19477                                     {
19478                                       tag:'span',
19479                                       cls: 'fc-event-title',
19480                                       html : String.format('{0}', ev.title)
19481                                     }
19482
19483
19484                                 ]
19485                             },
19486                             {
19487                                 cls: 'ui-resizable-handle ui-resizable-e',
19488                                 html : '&nbsp;&nbsp;&nbsp'
19489                             }
19490
19491                         ]
19492                     };
19493
19494                     if (i == 0) {
19495                         cfg.cls += ' fc-event-start';
19496                     }
19497                     if ((i+1) == rows.length) {
19498                         cfg.cls += ' fc-event-end';
19499                     }
19500
19501                     var ctr = _this.el.select('.fc-event-container',true).first();
19502                     var cg = ctr.createChild(cfg);
19503
19504                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19505                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19506
19507                     var r = (c.more.length) ? 1 : 0;
19508                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19509                     cg.setWidth(ebox.right - sbox.x -2);
19510
19511                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19512                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19513                     cg.on('click', _this.onEventClick, _this, ev);
19514
19515                     ev.els.push(cg);
19516                     
19517                 }
19518                 
19519             }
19520             
19521             
19522             if(c.more.length){
19523                 var  cfg = {
19524                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19525                     style : 'position: absolute',
19526                     unselectable : "on",
19527                     cn : [
19528                         {
19529                             cls: 'fc-event-inner',
19530                             cn : [
19531                                 {
19532                                   tag:'span',
19533                                   cls: 'fc-event-title',
19534                                   html : 'More'
19535                                 }
19536
19537
19538                             ]
19539                         },
19540                         {
19541                             cls: 'ui-resizable-handle ui-resizable-e',
19542                             html : '&nbsp;&nbsp;&nbsp'
19543                         }
19544
19545                     ]
19546                 };
19547
19548                 var ctr = _this.el.select('.fc-event-container',true).first();
19549                 var cg = ctr.createChild(cfg);
19550
19551                 var sbox = c.select('.fc-day-content',true).first().getBox();
19552                 var ebox = c.select('.fc-day-content',true).first().getBox();
19553                 //Roo.log(cg);
19554                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19555                 cg.setWidth(ebox.right - sbox.x -2);
19556
19557                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19558                 
19559             }
19560             
19561         });
19562         
19563         
19564         
19565     },
19566     
19567     onEventEnter: function (e, el,event,d) {
19568         this.fireEvent('evententer', this, el, event);
19569     },
19570     
19571     onEventLeave: function (e, el,event,d) {
19572         this.fireEvent('eventleave', this, el, event);
19573     },
19574     
19575     onEventClick: function (e, el,event,d) {
19576         this.fireEvent('eventclick', this, el, event);
19577     },
19578     
19579     onMonthChange: function () {
19580         this.store.load();
19581     },
19582     
19583     onMoreEventClick: function(e, el, more)
19584     {
19585         var _this = this;
19586         
19587         this.calpopover.placement = 'right';
19588         this.calpopover.setTitle('More');
19589         
19590         this.calpopover.setContent('');
19591         
19592         var ctr = this.calpopover.el.select('.popover-content', true).first();
19593         
19594         Roo.each(more, function(m){
19595             var cfg = {
19596                 cls : 'fc-event-hori fc-event-draggable',
19597                 html : m.title
19598             };
19599             var cg = ctr.createChild(cfg);
19600             
19601             cg.on('click', _this.onEventClick, _this, m);
19602         });
19603         
19604         this.calpopover.show(el);
19605         
19606         
19607     },
19608     
19609     onLoad: function () 
19610     {   
19611         this.calevents = [];
19612         var cal = this;
19613         
19614         if(this.store.getCount() > 0){
19615             this.store.data.each(function(d){
19616                cal.addItem({
19617                     id : d.data.id,
19618                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19619                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19620                     time : d.data.start_time,
19621                     title : d.data.title,
19622                     description : d.data.description,
19623                     venue : d.data.venue
19624                 });
19625             });
19626         }
19627         
19628         this.renderEvents();
19629         
19630         if(this.calevents.length && this.loadMask){
19631             this.maskEl.hide();
19632         }
19633     },
19634     
19635     onBeforeLoad: function()
19636     {
19637         this.clearEvents();
19638         if(this.loadMask){
19639             this.maskEl.show();
19640         }
19641     }
19642 });
19643
19644  
19645  /*
19646  * - LGPL
19647  *
19648  * element
19649  * 
19650  */
19651
19652 /**
19653  * @class Roo.bootstrap.Popover
19654  * @extends Roo.bootstrap.Component
19655  * Bootstrap Popover class
19656  * @cfg {String} html contents of the popover   (or false to use children..)
19657  * @cfg {String} title of popover (or false to hide)
19658  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19659  * @cfg {String} trigger click || hover (or false to trigger manually)
19660  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19661  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19662  *      - if false and it has a 'parent' then it will be automatically added to that element
19663  *      - if string - Roo.get  will be called 
19664  * @cfg {Number} delay - delay before showing
19665  
19666  * @constructor
19667  * Create a new Popover
19668  * @param {Object} config The config object
19669  */
19670
19671 Roo.bootstrap.Popover = function(config){
19672     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19673     
19674     this.addEvents({
19675         // raw events
19676          /**
19677          * @event show
19678          * After the popover show
19679          * 
19680          * @param {Roo.bootstrap.Popover} this
19681          */
19682         "show" : true,
19683         /**
19684          * @event hide
19685          * After the popover hide
19686          * 
19687          * @param {Roo.bootstrap.Popover} this
19688          */
19689         "hide" : true
19690     });
19691 };
19692
19693 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19694     
19695     title: false,
19696     html: false,
19697     
19698     placement : 'right',
19699     trigger : 'hover', // hover
19700     modal : false,
19701     delay : 0,
19702     
19703     over: false,
19704     
19705     can_build_overlaid : false,
19706     
19707     maskEl : false, // the mask element
19708     headerEl : false,
19709     contentEl : false,
19710     alignEl : false, // when show is called with an element - this get's stored.
19711     
19712     getChildContainer : function()
19713     {
19714         return this.contentEl;
19715         
19716     },
19717     getPopoverHeader : function()
19718     {
19719         this.title = true; // flag not to hide it..
19720         this.headerEl.addClass('p-0');
19721         return this.headerEl
19722     },
19723     
19724     
19725     getAutoCreate : function(){
19726          
19727         var cfg = {
19728            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19729            style: 'display:block',
19730            cn : [
19731                 {
19732                     cls : 'arrow'
19733                 },
19734                 {
19735                     cls : 'popover-inner ',
19736                     cn : [
19737                         {
19738                             tag: 'h3',
19739                             cls: 'popover-title popover-header',
19740                             html : this.title === false ? '' : this.title
19741                         },
19742                         {
19743                             cls : 'popover-content popover-body '  + (this.cls || ''),
19744                             html : this.html || ''
19745                         }
19746                     ]
19747                     
19748                 }
19749            ]
19750         };
19751         
19752         return cfg;
19753     },
19754     /**
19755      * @param {string} the title
19756      */
19757     setTitle: function(str)
19758     {
19759         this.title = str;
19760         if (this.el) {
19761             this.headerEl.dom.innerHTML = str;
19762         }
19763         
19764     },
19765     /**
19766      * @param {string} the body content
19767      */
19768     setContent: function(str)
19769     {
19770         this.html = str;
19771         if (this.contentEl) {
19772             this.contentEl.dom.innerHTML = str;
19773         }
19774         
19775     },
19776     // as it get's added to the bottom of the page.
19777     onRender : function(ct, position)
19778     {
19779         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19780         
19781         
19782         
19783         if(!this.el){
19784             var cfg = Roo.apply({},  this.getAutoCreate());
19785             cfg.id = Roo.id();
19786             
19787             if (this.cls) {
19788                 cfg.cls += ' ' + this.cls;
19789             }
19790             if (this.style) {
19791                 cfg.style = this.style;
19792             }
19793             //Roo.log("adding to ");
19794             this.el = Roo.get(document.body).createChild(cfg, position);
19795 //            Roo.log(this.el);
19796         }
19797         
19798         this.contentEl = this.el.select('.popover-content',true).first();
19799         this.headerEl =  this.el.select('.popover-title',true).first();
19800         
19801         var nitems = [];
19802         if(typeof(this.items) != 'undefined'){
19803             var items = this.items;
19804             delete this.items;
19805
19806             for(var i =0;i < items.length;i++) {
19807                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19808             }
19809         }
19810
19811         this.items = nitems;
19812         
19813         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19814         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19815         
19816         
19817         
19818         this.initEvents();
19819     },
19820     
19821     resizeMask : function()
19822     {
19823         this.maskEl.setSize(
19824             Roo.lib.Dom.getViewWidth(true),
19825             Roo.lib.Dom.getViewHeight(true)
19826         );
19827     },
19828     
19829     initEvents : function()
19830     {
19831         
19832         if (!this.modal) { 
19833             Roo.bootstrap.Popover.register(this);
19834         }
19835          
19836         this.arrowEl = this.el.select('.arrow',true).first();
19837         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19838         this.el.enableDisplayMode('block');
19839         this.el.hide();
19840  
19841         
19842         if (this.over === false && !this.parent()) {
19843             return; 
19844         }
19845         if (this.triggers === false) {
19846             return;
19847         }
19848          
19849         // support parent
19850         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19851         var triggers = this.trigger ? this.trigger.split(' ') : [];
19852         Roo.each(triggers, function(trigger) {
19853         
19854             if (trigger == 'click') {
19855                 on_el.on('click', this.toggle, this);
19856             } else if (trigger != 'manual') {
19857                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19858                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19859       
19860                 on_el.on(eventIn  ,this.enter, this);
19861                 on_el.on(eventOut, this.leave, this);
19862             }
19863         }, this);
19864     },
19865     
19866     
19867     // private
19868     timeout : null,
19869     hoverState : null,
19870     
19871     toggle : function () {
19872         this.hoverState == 'in' ? this.leave() : this.enter();
19873     },
19874     
19875     enter : function () {
19876         
19877         clearTimeout(this.timeout);
19878     
19879         this.hoverState = 'in';
19880     
19881         if (!this.delay || !this.delay.show) {
19882             this.show();
19883             return;
19884         }
19885         var _t = this;
19886         this.timeout = setTimeout(function () {
19887             if (_t.hoverState == 'in') {
19888                 _t.show();
19889             }
19890         }, this.delay.show)
19891     },
19892     
19893     leave : function() {
19894         clearTimeout(this.timeout);
19895     
19896         this.hoverState = 'out';
19897     
19898         if (!this.delay || !this.delay.hide) {
19899             this.hide();
19900             return;
19901         }
19902         var _t = this;
19903         this.timeout = setTimeout(function () {
19904             if (_t.hoverState == 'out') {
19905                 _t.hide();
19906             }
19907         }, this.delay.hide)
19908     },
19909     /**
19910      * Show the popover
19911      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19912      * @param {string} (left|right|top|bottom) position
19913      */
19914     show : function (on_el, placement)
19915     {
19916         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19917         on_el = on_el || false; // default to false
19918          
19919         if (!on_el) {
19920             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19921                 on_el = this.parent().el;
19922             } else if (this.over) {
19923                 Roo.get(this.over);
19924             }
19925             
19926         }
19927         
19928         this.alignEl = Roo.get( on_el );
19929
19930         if (!this.el) {
19931             this.render(document.body);
19932         }
19933         
19934         
19935          
19936         
19937         if (this.title === false) {
19938             this.headerEl.hide();
19939         }
19940         
19941        
19942         this.el.show();
19943         this.el.dom.style.display = 'block';
19944          
19945  
19946         if (this.alignEl) {
19947             this.updatePosition(this.placement, true);
19948              
19949         } else {
19950             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19951             var es = this.el.getSize();
19952             var x = Roo.lib.Dom.getViewWidth()/2;
19953             var y = Roo.lib.Dom.getViewHeight()/2;
19954             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19955             
19956         }
19957
19958         
19959         //var arrow = this.el.select('.arrow',true).first();
19960         //arrow.set(align[2], 
19961         
19962         this.el.addClass('in');
19963         
19964          
19965         
19966         this.hoverState = 'in';
19967         
19968         if (this.modal) {
19969             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19970             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19971             this.maskEl.dom.style.display = 'block';
19972             this.maskEl.addClass('show');
19973         }
19974         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19975  
19976         this.fireEvent('show', this);
19977         
19978     },
19979     /**
19980      * fire this manually after loading a grid in the table for example
19981      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19982      * @param {Boolean} try and move it if we cant get right position.
19983      */
19984     updatePosition : function(placement, try_move)
19985     {
19986         // allow for calling with no parameters
19987         placement = placement   ? placement :  this.placement;
19988         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19989         
19990         this.el.removeClass([
19991             'fade','top','bottom', 'left', 'right','in',
19992             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19993         ]);
19994         this.el.addClass(placement + ' bs-popover-' + placement);
19995         
19996         if (!this.alignEl ) {
19997             return false;
19998         }
19999         
20000         switch (placement) {
20001             case 'right':
20002                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
20003                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
20004                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20005                     //normal display... or moved up/down.
20006                     this.el.setXY(offset);
20007                     var xy = this.alignEl.getAnchorXY('tr', false);
20008                     xy[0]+=2;xy[1]+=5;
20009                     this.arrowEl.setXY(xy);
20010                     return true;
20011                 }
20012                 // continue through...
20013                 return this.updatePosition('left', false);
20014                 
20015             
20016             case 'left':
20017                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
20018                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
20019                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
20020                     //normal display... or moved up/down.
20021                     this.el.setXY(offset);
20022                     var xy = this.alignEl.getAnchorXY('tl', false);
20023                     xy[0]-=10;xy[1]+=5; // << fix me
20024                     this.arrowEl.setXY(xy);
20025                     return true;
20026                 }
20027                 // call self...
20028                 return this.updatePosition('right', false);
20029             
20030             case 'top':
20031                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20032                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20033                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20034                     //normal display... or moved up/down.
20035                     this.el.setXY(offset);
20036                     var xy = this.alignEl.getAnchorXY('t', false);
20037                     xy[1]-=10; // << fix me
20038                     this.arrowEl.setXY(xy);
20039                     return true;
20040                 }
20041                 // fall through
20042                return this.updatePosition('bottom', false);
20043             
20044             case 'bottom':
20045                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20046                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20047                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20048                     //normal display... or moved up/down.
20049                     this.el.setXY(offset);
20050                     var xy = this.alignEl.getAnchorXY('b', false);
20051                      xy[1]+=2; // << fix me
20052                     this.arrowEl.setXY(xy);
20053                     return true;
20054                 }
20055                 // fall through
20056                 return this.updatePosition('top', false);
20057                 
20058             
20059         }
20060         
20061         
20062         return false;
20063     },
20064     
20065     hide : function()
20066     {
20067         this.el.setXY([0,0]);
20068         this.el.removeClass('in');
20069         this.el.hide();
20070         this.hoverState = null;
20071         this.maskEl.hide(); // always..
20072         this.fireEvent('hide', this);
20073     }
20074     
20075 });
20076
20077
20078 Roo.apply(Roo.bootstrap.Popover, {
20079
20080     alignment : {
20081         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20082         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20083         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20084         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20085     },
20086     
20087     zIndex : 20001,
20088
20089     clickHander : false,
20090     
20091
20092     onMouseDown : function(e)
20093     {
20094         if (!e.getTarget(".roo-popover")) {
20095             this.hideAll();
20096         }
20097          
20098     },
20099     
20100     popups : [],
20101     
20102     register : function(popup)
20103     {
20104         if (!Roo.bootstrap.Popover.clickHandler) {
20105             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20106         }
20107         // hide other popups.
20108         this.hideAll();
20109         this.popups.push(popup);
20110     },
20111     hideAll : function()
20112     {
20113         this.popups.forEach(function(p) {
20114             p.hide();
20115         });
20116     }
20117
20118 });/*
20119  * - LGPL
20120  *
20121  * Card header - holder for the card header elements.
20122  * 
20123  */
20124
20125 /**
20126  * @class Roo.bootstrap.PopoverNav
20127  * @extends Roo.bootstrap.NavGroup
20128  * Bootstrap Popover header navigation class
20129  * @constructor
20130  * Create a new Popover Header Navigation 
20131  * @param {Object} config The config object
20132  */
20133
20134 Roo.bootstrap.PopoverNav = function(config){
20135     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20136 };
20137
20138 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20139     
20140     
20141     container_method : 'getPopoverHeader' 
20142     
20143      
20144     
20145     
20146    
20147 });
20148
20149  
20150
20151  /*
20152  * - LGPL
20153  *
20154  * Progress
20155  * 
20156  */
20157
20158 /**
20159  * @class Roo.bootstrap.Progress
20160  * @extends Roo.bootstrap.Component
20161  * Bootstrap Progress class
20162  * @cfg {Boolean} striped striped of the progress bar
20163  * @cfg {Boolean} active animated of the progress bar
20164  * 
20165  * 
20166  * @constructor
20167  * Create a new Progress
20168  * @param {Object} config The config object
20169  */
20170
20171 Roo.bootstrap.Progress = function(config){
20172     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20173 };
20174
20175 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20176     
20177     striped : false,
20178     active: false,
20179     
20180     getAutoCreate : function(){
20181         var cfg = {
20182             tag: 'div',
20183             cls: 'progress'
20184         };
20185         
20186         
20187         if(this.striped){
20188             cfg.cls += ' progress-striped';
20189         }
20190       
20191         if(this.active){
20192             cfg.cls += ' active';
20193         }
20194         
20195         
20196         return cfg;
20197     }
20198    
20199 });
20200
20201  
20202
20203  /*
20204  * - LGPL
20205  *
20206  * ProgressBar
20207  * 
20208  */
20209
20210 /**
20211  * @class Roo.bootstrap.ProgressBar
20212  * @extends Roo.bootstrap.Component
20213  * Bootstrap ProgressBar class
20214  * @cfg {Number} aria_valuenow aria-value now
20215  * @cfg {Number} aria_valuemin aria-value min
20216  * @cfg {Number} aria_valuemax aria-value max
20217  * @cfg {String} label label for the progress bar
20218  * @cfg {String} panel (success | info | warning | danger )
20219  * @cfg {String} role role of the progress bar
20220  * @cfg {String} sr_only text
20221  * 
20222  * 
20223  * @constructor
20224  * Create a new ProgressBar
20225  * @param {Object} config The config object
20226  */
20227
20228 Roo.bootstrap.ProgressBar = function(config){
20229     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20230 };
20231
20232 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20233     
20234     aria_valuenow : 0,
20235     aria_valuemin : 0,
20236     aria_valuemax : 100,
20237     label : false,
20238     panel : false,
20239     role : false,
20240     sr_only: false,
20241     
20242     getAutoCreate : function()
20243     {
20244         
20245         var cfg = {
20246             tag: 'div',
20247             cls: 'progress-bar',
20248             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20249         };
20250         
20251         if(this.sr_only){
20252             cfg.cn = {
20253                 tag: 'span',
20254                 cls: 'sr-only',
20255                 html: this.sr_only
20256             }
20257         }
20258         
20259         if(this.role){
20260             cfg.role = this.role;
20261         }
20262         
20263         if(this.aria_valuenow){
20264             cfg['aria-valuenow'] = this.aria_valuenow;
20265         }
20266         
20267         if(this.aria_valuemin){
20268             cfg['aria-valuemin'] = this.aria_valuemin;
20269         }
20270         
20271         if(this.aria_valuemax){
20272             cfg['aria-valuemax'] = this.aria_valuemax;
20273         }
20274         
20275         if(this.label && !this.sr_only){
20276             cfg.html = this.label;
20277         }
20278         
20279         if(this.panel){
20280             cfg.cls += ' progress-bar-' + this.panel;
20281         }
20282         
20283         return cfg;
20284     },
20285     
20286     update : function(aria_valuenow)
20287     {
20288         this.aria_valuenow = aria_valuenow;
20289         
20290         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20291     }
20292    
20293 });
20294
20295  
20296
20297  /*
20298  * - LGPL
20299  *
20300  * column
20301  * 
20302  */
20303
20304 /**
20305  * @class Roo.bootstrap.TabGroup
20306  * @extends Roo.bootstrap.Column
20307  * Bootstrap Column class
20308  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20309  * @cfg {Boolean} carousel true to make the group behave like a carousel
20310  * @cfg {Boolean} bullets show bullets for the panels
20311  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20312  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20313  * @cfg {Boolean} showarrow (true|false) show arrow default true
20314  * 
20315  * @constructor
20316  * Create a new TabGroup
20317  * @param {Object} config The config object
20318  */
20319
20320 Roo.bootstrap.TabGroup = function(config){
20321     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20322     if (!this.navId) {
20323         this.navId = Roo.id();
20324     }
20325     this.tabs = [];
20326     Roo.bootstrap.TabGroup.register(this);
20327     
20328 };
20329
20330 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20331     
20332     carousel : false,
20333     transition : false,
20334     bullets : 0,
20335     timer : 0,
20336     autoslide : false,
20337     slideFn : false,
20338     slideOnTouch : false,
20339     showarrow : true,
20340     
20341     getAutoCreate : function()
20342     {
20343         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20344         
20345         cfg.cls += ' tab-content';
20346         
20347         if (this.carousel) {
20348             cfg.cls += ' carousel slide';
20349             
20350             cfg.cn = [{
20351                cls : 'carousel-inner',
20352                cn : []
20353             }];
20354         
20355             if(this.bullets  && !Roo.isTouch){
20356                 
20357                 var bullets = {
20358                     cls : 'carousel-bullets',
20359                     cn : []
20360                 };
20361                
20362                 if(this.bullets_cls){
20363                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20364                 }
20365                 
20366                 bullets.cn.push({
20367                     cls : 'clear'
20368                 });
20369                 
20370                 cfg.cn[0].cn.push(bullets);
20371             }
20372             
20373             if(this.showarrow){
20374                 cfg.cn[0].cn.push({
20375                     tag : 'div',
20376                     class : 'carousel-arrow',
20377                     cn : [
20378                         {
20379                             tag : 'div',
20380                             class : 'carousel-prev',
20381                             cn : [
20382                                 {
20383                                     tag : 'i',
20384                                     class : 'fa fa-chevron-left'
20385                                 }
20386                             ]
20387                         },
20388                         {
20389                             tag : 'div',
20390                             class : 'carousel-next',
20391                             cn : [
20392                                 {
20393                                     tag : 'i',
20394                                     class : 'fa fa-chevron-right'
20395                                 }
20396                             ]
20397                         }
20398                     ]
20399                 });
20400             }
20401             
20402         }
20403         
20404         return cfg;
20405     },
20406     
20407     initEvents:  function()
20408     {
20409 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20410 //            this.el.on("touchstart", this.onTouchStart, this);
20411 //        }
20412         
20413         if(this.autoslide){
20414             var _this = this;
20415             
20416             this.slideFn = window.setInterval(function() {
20417                 _this.showPanelNext();
20418             }, this.timer);
20419         }
20420         
20421         if(this.showarrow){
20422             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20423             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20424         }
20425         
20426         
20427     },
20428     
20429 //    onTouchStart : function(e, el, o)
20430 //    {
20431 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20432 //            return;
20433 //        }
20434 //        
20435 //        this.showPanelNext();
20436 //    },
20437     
20438     
20439     getChildContainer : function()
20440     {
20441         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20442     },
20443     
20444     /**
20445     * register a Navigation item
20446     * @param {Roo.bootstrap.NavItem} the navitem to add
20447     */
20448     register : function(item)
20449     {
20450         this.tabs.push( item);
20451         item.navId = this.navId; // not really needed..
20452         this.addBullet();
20453     
20454     },
20455     
20456     getActivePanel : function()
20457     {
20458         var r = false;
20459         Roo.each(this.tabs, function(t) {
20460             if (t.active) {
20461                 r = t;
20462                 return false;
20463             }
20464             return null;
20465         });
20466         return r;
20467         
20468     },
20469     getPanelByName : function(n)
20470     {
20471         var r = false;
20472         Roo.each(this.tabs, function(t) {
20473             if (t.tabId == n) {
20474                 r = t;
20475                 return false;
20476             }
20477             return null;
20478         });
20479         return r;
20480     },
20481     indexOfPanel : function(p)
20482     {
20483         var r = false;
20484         Roo.each(this.tabs, function(t,i) {
20485             if (t.tabId == p.tabId) {
20486                 r = i;
20487                 return false;
20488             }
20489             return null;
20490         });
20491         return r;
20492     },
20493     /**
20494      * show a specific panel
20495      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20496      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20497      */
20498     showPanel : function (pan)
20499     {
20500         if(this.transition || typeof(pan) == 'undefined'){
20501             Roo.log("waiting for the transitionend");
20502             return false;
20503         }
20504         
20505         if (typeof(pan) == 'number') {
20506             pan = this.tabs[pan];
20507         }
20508         
20509         if (typeof(pan) == 'string') {
20510             pan = this.getPanelByName(pan);
20511         }
20512         
20513         var cur = this.getActivePanel();
20514         
20515         if(!pan || !cur){
20516             Roo.log('pan or acitve pan is undefined');
20517             return false;
20518         }
20519         
20520         if (pan.tabId == this.getActivePanel().tabId) {
20521             return true;
20522         }
20523         
20524         if (false === cur.fireEvent('beforedeactivate')) {
20525             return false;
20526         }
20527         
20528         if(this.bullets > 0 && !Roo.isTouch){
20529             this.setActiveBullet(this.indexOfPanel(pan));
20530         }
20531         
20532         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20533             
20534             //class="carousel-item carousel-item-next carousel-item-left"
20535             
20536             this.transition = true;
20537             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20538             var lr = dir == 'next' ? 'left' : 'right';
20539             pan.el.addClass(dir); // or prev
20540             pan.el.addClass('carousel-item-' + dir); // or prev
20541             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20542             cur.el.addClass(lr); // or right
20543             pan.el.addClass(lr);
20544             cur.el.addClass('carousel-item-' +lr); // or right
20545             pan.el.addClass('carousel-item-' +lr);
20546             
20547             
20548             var _this = this;
20549             cur.el.on('transitionend', function() {
20550                 Roo.log("trans end?");
20551                 
20552                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20553                 pan.setActive(true);
20554                 
20555                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20556                 cur.setActive(false);
20557                 
20558                 _this.transition = false;
20559                 
20560             }, this, { single:  true } );
20561             
20562             return true;
20563         }
20564         
20565         cur.setActive(false);
20566         pan.setActive(true);
20567         
20568         return true;
20569         
20570     },
20571     showPanelNext : function()
20572     {
20573         var i = this.indexOfPanel(this.getActivePanel());
20574         
20575         if (i >= this.tabs.length - 1 && !this.autoslide) {
20576             return;
20577         }
20578         
20579         if (i >= this.tabs.length - 1 && this.autoslide) {
20580             i = -1;
20581         }
20582         
20583         this.showPanel(this.tabs[i+1]);
20584     },
20585     
20586     showPanelPrev : function()
20587     {
20588         var i = this.indexOfPanel(this.getActivePanel());
20589         
20590         if (i  < 1 && !this.autoslide) {
20591             return;
20592         }
20593         
20594         if (i < 1 && this.autoslide) {
20595             i = this.tabs.length;
20596         }
20597         
20598         this.showPanel(this.tabs[i-1]);
20599     },
20600     
20601     
20602     addBullet: function()
20603     {
20604         if(!this.bullets || Roo.isTouch){
20605             return;
20606         }
20607         var ctr = this.el.select('.carousel-bullets',true).first();
20608         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20609         var bullet = ctr.createChild({
20610             cls : 'bullet bullet-' + i
20611         },ctr.dom.lastChild);
20612         
20613         
20614         var _this = this;
20615         
20616         bullet.on('click', (function(e, el, o, ii, t){
20617
20618             e.preventDefault();
20619
20620             this.showPanel(ii);
20621
20622             if(this.autoslide && this.slideFn){
20623                 clearInterval(this.slideFn);
20624                 this.slideFn = window.setInterval(function() {
20625                     _this.showPanelNext();
20626                 }, this.timer);
20627             }
20628
20629         }).createDelegate(this, [i, bullet], true));
20630                 
20631         
20632     },
20633      
20634     setActiveBullet : function(i)
20635     {
20636         if(Roo.isTouch){
20637             return;
20638         }
20639         
20640         Roo.each(this.el.select('.bullet', true).elements, function(el){
20641             el.removeClass('selected');
20642         });
20643
20644         var bullet = this.el.select('.bullet-' + i, true).first();
20645         
20646         if(!bullet){
20647             return;
20648         }
20649         
20650         bullet.addClass('selected');
20651     }
20652     
20653     
20654   
20655 });
20656
20657  
20658
20659  
20660  
20661 Roo.apply(Roo.bootstrap.TabGroup, {
20662     
20663     groups: {},
20664      /**
20665     * register a Navigation Group
20666     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20667     */
20668     register : function(navgrp)
20669     {
20670         this.groups[navgrp.navId] = navgrp;
20671         
20672     },
20673     /**
20674     * fetch a Navigation Group based on the navigation ID
20675     * if one does not exist , it will get created.
20676     * @param {string} the navgroup to add
20677     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20678     */
20679     get: function(navId) {
20680         if (typeof(this.groups[navId]) == 'undefined') {
20681             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20682         }
20683         return this.groups[navId] ;
20684     }
20685     
20686     
20687     
20688 });
20689
20690  /*
20691  * - LGPL
20692  *
20693  * TabPanel
20694  * 
20695  */
20696
20697 /**
20698  * @class Roo.bootstrap.TabPanel
20699  * @extends Roo.bootstrap.Component
20700  * Bootstrap TabPanel class
20701  * @cfg {Boolean} active panel active
20702  * @cfg {String} html panel content
20703  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20704  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20705  * @cfg {String} href click to link..
20706  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20707  * 
20708  * 
20709  * @constructor
20710  * Create a new TabPanel
20711  * @param {Object} config The config object
20712  */
20713
20714 Roo.bootstrap.TabPanel = function(config){
20715     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20716     this.addEvents({
20717         /**
20718              * @event changed
20719              * Fires when the active status changes
20720              * @param {Roo.bootstrap.TabPanel} this
20721              * @param {Boolean} state the new state
20722             
20723          */
20724         'changed': true,
20725         /**
20726              * @event beforedeactivate
20727              * Fires before a tab is de-activated - can be used to do validation on a form.
20728              * @param {Roo.bootstrap.TabPanel} this
20729              * @return {Boolean} false if there is an error
20730             
20731          */
20732         'beforedeactivate': true
20733      });
20734     
20735     this.tabId = this.tabId || Roo.id();
20736   
20737 };
20738
20739 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20740     
20741     active: false,
20742     html: false,
20743     tabId: false,
20744     navId : false,
20745     href : '',
20746     touchSlide : false,
20747     getAutoCreate : function(){
20748         
20749         
20750         var cfg = {
20751             tag: 'div',
20752             // item is needed for carousel - not sure if it has any effect otherwise
20753             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20754             html: this.html || ''
20755         };
20756         
20757         if(this.active){
20758             cfg.cls += ' active';
20759         }
20760         
20761         if(this.tabId){
20762             cfg.tabId = this.tabId;
20763         }
20764         
20765         
20766         
20767         return cfg;
20768     },
20769     
20770     initEvents:  function()
20771     {
20772         var p = this.parent();
20773         
20774         this.navId = this.navId || p.navId;
20775         
20776         if (typeof(this.navId) != 'undefined') {
20777             // not really needed.. but just in case.. parent should be a NavGroup.
20778             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20779             
20780             tg.register(this);
20781             
20782             var i = tg.tabs.length - 1;
20783             
20784             if(this.active && tg.bullets > 0 && i < tg.bullets){
20785                 tg.setActiveBullet(i);
20786             }
20787         }
20788         
20789         this.el.on('click', this.onClick, this);
20790         
20791         if(Roo.isTouch && this.touchSlide){
20792             this.el.on("touchstart", this.onTouchStart, this);
20793             this.el.on("touchmove", this.onTouchMove, this);
20794             this.el.on("touchend", this.onTouchEnd, this);
20795         }
20796         
20797     },
20798     
20799     onRender : function(ct, position)
20800     {
20801         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20802     },
20803     
20804     setActive : function(state)
20805     {
20806         Roo.log("panel - set active " + this.tabId + "=" + state);
20807         
20808         this.active = state;
20809         if (!state) {
20810             this.el.removeClass('active');
20811             
20812         } else  if (!this.el.hasClass('active')) {
20813             this.el.addClass('active');
20814         }
20815         
20816         this.fireEvent('changed', this, state);
20817     },
20818     
20819     onClick : function(e)
20820     {
20821         e.preventDefault();
20822         
20823         if(!this.href.length){
20824             return;
20825         }
20826         
20827         window.location.href = this.href;
20828     },
20829     
20830     startX : 0,
20831     startY : 0,
20832     endX : 0,
20833     endY : 0,
20834     swiping : false,
20835     
20836     onTouchStart : function(e)
20837     {
20838         this.swiping = false;
20839         
20840         this.startX = e.browserEvent.touches[0].clientX;
20841         this.startY = e.browserEvent.touches[0].clientY;
20842     },
20843     
20844     onTouchMove : function(e)
20845     {
20846         this.swiping = true;
20847         
20848         this.endX = e.browserEvent.touches[0].clientX;
20849         this.endY = e.browserEvent.touches[0].clientY;
20850     },
20851     
20852     onTouchEnd : function(e)
20853     {
20854         if(!this.swiping){
20855             this.onClick(e);
20856             return;
20857         }
20858         
20859         var tabGroup = this.parent();
20860         
20861         if(this.endX > this.startX){ // swiping right
20862             tabGroup.showPanelPrev();
20863             return;
20864         }
20865         
20866         if(this.startX > this.endX){ // swiping left
20867             tabGroup.showPanelNext();
20868             return;
20869         }
20870     }
20871     
20872     
20873 });
20874  
20875
20876  
20877
20878  /*
20879  * - LGPL
20880  *
20881  * DateField
20882  * 
20883  */
20884
20885 /**
20886  * @class Roo.bootstrap.DateField
20887  * @extends Roo.bootstrap.Input
20888  * Bootstrap DateField class
20889  * @cfg {Number} weekStart default 0
20890  * @cfg {String} viewMode default empty, (months|years)
20891  * @cfg {String} minViewMode default empty, (months|years)
20892  * @cfg {Number} startDate default -Infinity
20893  * @cfg {Number} endDate default Infinity
20894  * @cfg {Boolean} todayHighlight default false
20895  * @cfg {Boolean} todayBtn default false
20896  * @cfg {Boolean} calendarWeeks default false
20897  * @cfg {Object} daysOfWeekDisabled default empty
20898  * @cfg {Boolean} singleMode default false (true | false)
20899  * 
20900  * @cfg {Boolean} keyboardNavigation default true
20901  * @cfg {String} language default en
20902  * 
20903  * @constructor
20904  * Create a new DateField
20905  * @param {Object} config The config object
20906  */
20907
20908 Roo.bootstrap.DateField = function(config){
20909     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20910      this.addEvents({
20911             /**
20912              * @event show
20913              * Fires when this field show.
20914              * @param {Roo.bootstrap.DateField} this
20915              * @param {Mixed} date The date value
20916              */
20917             show : true,
20918             /**
20919              * @event show
20920              * Fires when this field hide.
20921              * @param {Roo.bootstrap.DateField} this
20922              * @param {Mixed} date The date value
20923              */
20924             hide : true,
20925             /**
20926              * @event select
20927              * Fires when select a date.
20928              * @param {Roo.bootstrap.DateField} this
20929              * @param {Mixed} date The date value
20930              */
20931             select : true,
20932             /**
20933              * @event beforeselect
20934              * Fires when before select a date.
20935              * @param {Roo.bootstrap.DateField} this
20936              * @param {Mixed} date The date value
20937              */
20938             beforeselect : true
20939         });
20940 };
20941
20942 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20943     
20944     /**
20945      * @cfg {String} format
20946      * The default date format string which can be overriden for localization support.  The format must be
20947      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20948      */
20949     format : "m/d/y",
20950     /**
20951      * @cfg {String} altFormats
20952      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20953      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20954      */
20955     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20956     
20957     weekStart : 0,
20958     
20959     viewMode : '',
20960     
20961     minViewMode : '',
20962     
20963     todayHighlight : false,
20964     
20965     todayBtn: false,
20966     
20967     language: 'en',
20968     
20969     keyboardNavigation: true,
20970     
20971     calendarWeeks: false,
20972     
20973     startDate: -Infinity,
20974     
20975     endDate: Infinity,
20976     
20977     daysOfWeekDisabled: [],
20978     
20979     _events: [],
20980     
20981     singleMode : false,
20982     
20983     UTCDate: function()
20984     {
20985         return new Date(Date.UTC.apply(Date, arguments));
20986     },
20987     
20988     UTCToday: function()
20989     {
20990         var today = new Date();
20991         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20992     },
20993     
20994     getDate: function() {
20995             var d = this.getUTCDate();
20996             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20997     },
20998     
20999     getUTCDate: function() {
21000             return this.date;
21001     },
21002     
21003     setDate: function(d) {
21004             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
21005     },
21006     
21007     setUTCDate: function(d) {
21008             this.date = d;
21009             this.setValue(this.formatDate(this.date));
21010     },
21011         
21012     onRender: function(ct, position)
21013     {
21014         
21015         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
21016         
21017         this.language = this.language || 'en';
21018         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
21019         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
21020         
21021         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21022         this.format = this.format || 'm/d/y';
21023         this.isInline = false;
21024         this.isInput = true;
21025         this.component = this.el.select('.add-on', true).first() || false;
21026         this.component = (this.component && this.component.length === 0) ? false : this.component;
21027         this.hasInput = this.component && this.inputEl().length;
21028         
21029         if (typeof(this.minViewMode === 'string')) {
21030             switch (this.minViewMode) {
21031                 case 'months':
21032                     this.minViewMode = 1;
21033                     break;
21034                 case 'years':
21035                     this.minViewMode = 2;
21036                     break;
21037                 default:
21038                     this.minViewMode = 0;
21039                     break;
21040             }
21041         }
21042         
21043         if (typeof(this.viewMode === 'string')) {
21044             switch (this.viewMode) {
21045                 case 'months':
21046                     this.viewMode = 1;
21047                     break;
21048                 case 'years':
21049                     this.viewMode = 2;
21050                     break;
21051                 default:
21052                     this.viewMode = 0;
21053                     break;
21054             }
21055         }
21056                 
21057         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21058         
21059 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21060         
21061         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21062         
21063         this.picker().on('mousedown', this.onMousedown, this);
21064         this.picker().on('click', this.onClick, this);
21065         
21066         this.picker().addClass('datepicker-dropdown');
21067         
21068         this.startViewMode = this.viewMode;
21069         
21070         if(this.singleMode){
21071             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21072                 v.setVisibilityMode(Roo.Element.DISPLAY);
21073                 v.hide();
21074             });
21075             
21076             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21077                 v.setStyle('width', '189px');
21078             });
21079         }
21080         
21081         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21082             if(!this.calendarWeeks){
21083                 v.remove();
21084                 return;
21085             }
21086             
21087             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21088             v.attr('colspan', function(i, val){
21089                 return parseInt(val) + 1;
21090             });
21091         });
21092                         
21093         
21094         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21095         
21096         this.setStartDate(this.startDate);
21097         this.setEndDate(this.endDate);
21098         
21099         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21100         
21101         this.fillDow();
21102         this.fillMonths();
21103         this.update();
21104         this.showMode();
21105         
21106         if(this.isInline) {
21107             this.showPopup();
21108         }
21109     },
21110     
21111     picker : function()
21112     {
21113         return this.pickerEl;
21114 //        return this.el.select('.datepicker', true).first();
21115     },
21116     
21117     fillDow: function()
21118     {
21119         var dowCnt = this.weekStart;
21120         
21121         var dow = {
21122             tag: 'tr',
21123             cn: [
21124                 
21125             ]
21126         };
21127         
21128         if(this.calendarWeeks){
21129             dow.cn.push({
21130                 tag: 'th',
21131                 cls: 'cw',
21132                 html: '&nbsp;'
21133             })
21134         }
21135         
21136         while (dowCnt < this.weekStart + 7) {
21137             dow.cn.push({
21138                 tag: 'th',
21139                 cls: 'dow',
21140                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21141             });
21142         }
21143         
21144         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21145     },
21146     
21147     fillMonths: function()
21148     {    
21149         var i = 0;
21150         var months = this.picker().select('>.datepicker-months td', true).first();
21151         
21152         months.dom.innerHTML = '';
21153         
21154         while (i < 12) {
21155             var month = {
21156                 tag: 'span',
21157                 cls: 'month',
21158                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21159             };
21160             
21161             months.createChild(month);
21162         }
21163         
21164     },
21165     
21166     update: function()
21167     {
21168         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;
21169         
21170         if (this.date < this.startDate) {
21171             this.viewDate = new Date(this.startDate);
21172         } else if (this.date > this.endDate) {
21173             this.viewDate = new Date(this.endDate);
21174         } else {
21175             this.viewDate = new Date(this.date);
21176         }
21177         
21178         this.fill();
21179     },
21180     
21181     fill: function() 
21182     {
21183         var d = new Date(this.viewDate),
21184                 year = d.getUTCFullYear(),
21185                 month = d.getUTCMonth(),
21186                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21187                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21188                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21189                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21190                 currentDate = this.date && this.date.valueOf(),
21191                 today = this.UTCToday();
21192         
21193         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21194         
21195 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21196         
21197 //        this.picker.select('>tfoot th.today').
21198 //                                              .text(dates[this.language].today)
21199 //                                              .toggle(this.todayBtn !== false);
21200     
21201         this.updateNavArrows();
21202         this.fillMonths();
21203                                                 
21204         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21205         
21206         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21207          
21208         prevMonth.setUTCDate(day);
21209         
21210         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21211         
21212         var nextMonth = new Date(prevMonth);
21213         
21214         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21215         
21216         nextMonth = nextMonth.valueOf();
21217         
21218         var fillMonths = false;
21219         
21220         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21221         
21222         while(prevMonth.valueOf() <= nextMonth) {
21223             var clsName = '';
21224             
21225             if (prevMonth.getUTCDay() === this.weekStart) {
21226                 if(fillMonths){
21227                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21228                 }
21229                     
21230                 fillMonths = {
21231                     tag: 'tr',
21232                     cn: []
21233                 };
21234                 
21235                 if(this.calendarWeeks){
21236                     // ISO 8601: First week contains first thursday.
21237                     // ISO also states week starts on Monday, but we can be more abstract here.
21238                     var
21239                     // Start of current week: based on weekstart/current date
21240                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21241                     // Thursday of this week
21242                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21243                     // First Thursday of year, year from thursday
21244                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21245                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21246                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21247                     
21248                     fillMonths.cn.push({
21249                         tag: 'td',
21250                         cls: 'cw',
21251                         html: calWeek
21252                     });
21253                 }
21254             }
21255             
21256             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21257                 clsName += ' old';
21258             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21259                 clsName += ' new';
21260             }
21261             if (this.todayHighlight &&
21262                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21263                 prevMonth.getUTCMonth() == today.getMonth() &&
21264                 prevMonth.getUTCDate() == today.getDate()) {
21265                 clsName += ' today';
21266             }
21267             
21268             if (currentDate && prevMonth.valueOf() === currentDate) {
21269                 clsName += ' active';
21270             }
21271             
21272             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21273                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21274                     clsName += ' disabled';
21275             }
21276             
21277             fillMonths.cn.push({
21278                 tag: 'td',
21279                 cls: 'day ' + clsName,
21280                 html: prevMonth.getDate()
21281             });
21282             
21283             prevMonth.setDate(prevMonth.getDate()+1);
21284         }
21285           
21286         var currentYear = this.date && this.date.getUTCFullYear();
21287         var currentMonth = this.date && this.date.getUTCMonth();
21288         
21289         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21290         
21291         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21292             v.removeClass('active');
21293             
21294             if(currentYear === year && k === currentMonth){
21295                 v.addClass('active');
21296             }
21297             
21298             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21299                 v.addClass('disabled');
21300             }
21301             
21302         });
21303         
21304         
21305         year = parseInt(year/10, 10) * 10;
21306         
21307         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21308         
21309         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21310         
21311         year -= 1;
21312         for (var i = -1; i < 11; i++) {
21313             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21314                 tag: 'span',
21315                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21316                 html: year
21317             });
21318             
21319             year += 1;
21320         }
21321     },
21322     
21323     showMode: function(dir) 
21324     {
21325         if (dir) {
21326             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21327         }
21328         
21329         Roo.each(this.picker().select('>div',true).elements, function(v){
21330             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21331             v.hide();
21332         });
21333         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21334     },
21335     
21336     place: function()
21337     {
21338         if(this.isInline) {
21339             return;
21340         }
21341         
21342         this.picker().removeClass(['bottom', 'top']);
21343         
21344         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21345             /*
21346              * place to the top of element!
21347              *
21348              */
21349             
21350             this.picker().addClass('top');
21351             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21352             
21353             return;
21354         }
21355         
21356         this.picker().addClass('bottom');
21357         
21358         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21359     },
21360     
21361     parseDate : function(value)
21362     {
21363         if(!value || value instanceof Date){
21364             return value;
21365         }
21366         var v = Date.parseDate(value, this.format);
21367         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21368             v = Date.parseDate(value, 'Y-m-d');
21369         }
21370         if(!v && this.altFormats){
21371             if(!this.altFormatsArray){
21372                 this.altFormatsArray = this.altFormats.split("|");
21373             }
21374             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21375                 v = Date.parseDate(value, this.altFormatsArray[i]);
21376             }
21377         }
21378         return v;
21379     },
21380     
21381     formatDate : function(date, fmt)
21382     {   
21383         return (!date || !(date instanceof Date)) ?
21384         date : date.dateFormat(fmt || this.format);
21385     },
21386     
21387     onFocus : function()
21388     {
21389         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21390         this.showPopup();
21391     },
21392     
21393     onBlur : function()
21394     {
21395         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21396         
21397         var d = this.inputEl().getValue();
21398         
21399         this.setValue(d);
21400                 
21401         this.hidePopup();
21402     },
21403     
21404     showPopup : function()
21405     {
21406         this.picker().show();
21407         this.update();
21408         this.place();
21409         
21410         this.fireEvent('showpopup', this, this.date);
21411     },
21412     
21413     hidePopup : function()
21414     {
21415         if(this.isInline) {
21416             return;
21417         }
21418         this.picker().hide();
21419         this.viewMode = this.startViewMode;
21420         this.showMode();
21421         
21422         this.fireEvent('hidepopup', this, this.date);
21423         
21424     },
21425     
21426     onMousedown: function(e)
21427     {
21428         e.stopPropagation();
21429         e.preventDefault();
21430     },
21431     
21432     keyup: function(e)
21433     {
21434         Roo.bootstrap.DateField.superclass.keyup.call(this);
21435         this.update();
21436     },
21437
21438     setValue: function(v)
21439     {
21440         if(this.fireEvent('beforeselect', this, v) !== false){
21441             var d = new Date(this.parseDate(v) ).clearTime();
21442         
21443             if(isNaN(d.getTime())){
21444                 this.date = this.viewDate = '';
21445                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21446                 return;
21447             }
21448
21449             v = this.formatDate(d);
21450
21451             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21452
21453             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21454
21455             this.update();
21456
21457             this.fireEvent('select', this, this.date);
21458         }
21459     },
21460     
21461     getValue: function()
21462     {
21463         return this.formatDate(this.date);
21464     },
21465     
21466     fireKey: function(e)
21467     {
21468         if (!this.picker().isVisible()){
21469             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21470                 this.showPopup();
21471             }
21472             return;
21473         }
21474         
21475         var dateChanged = false,
21476         dir, day, month,
21477         newDate, newViewDate;
21478         
21479         switch(e.keyCode){
21480             case 27: // escape
21481                 this.hidePopup();
21482                 e.preventDefault();
21483                 break;
21484             case 37: // left
21485             case 39: // right
21486                 if (!this.keyboardNavigation) {
21487                     break;
21488                 }
21489                 dir = e.keyCode == 37 ? -1 : 1;
21490                 
21491                 if (e.ctrlKey){
21492                     newDate = this.moveYear(this.date, dir);
21493                     newViewDate = this.moveYear(this.viewDate, dir);
21494                 } else if (e.shiftKey){
21495                     newDate = this.moveMonth(this.date, dir);
21496                     newViewDate = this.moveMonth(this.viewDate, dir);
21497                 } else {
21498                     newDate = new Date(this.date);
21499                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21500                     newViewDate = new Date(this.viewDate);
21501                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21502                 }
21503                 if (this.dateWithinRange(newDate)){
21504                     this.date = newDate;
21505                     this.viewDate = newViewDate;
21506                     this.setValue(this.formatDate(this.date));
21507 //                    this.update();
21508                     e.preventDefault();
21509                     dateChanged = true;
21510                 }
21511                 break;
21512             case 38: // up
21513             case 40: // down
21514                 if (!this.keyboardNavigation) {
21515                     break;
21516                 }
21517                 dir = e.keyCode == 38 ? -1 : 1;
21518                 if (e.ctrlKey){
21519                     newDate = this.moveYear(this.date, dir);
21520                     newViewDate = this.moveYear(this.viewDate, dir);
21521                 } else if (e.shiftKey){
21522                     newDate = this.moveMonth(this.date, dir);
21523                     newViewDate = this.moveMonth(this.viewDate, dir);
21524                 } else {
21525                     newDate = new Date(this.date);
21526                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21527                     newViewDate = new Date(this.viewDate);
21528                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21529                 }
21530                 if (this.dateWithinRange(newDate)){
21531                     this.date = newDate;
21532                     this.viewDate = newViewDate;
21533                     this.setValue(this.formatDate(this.date));
21534 //                    this.update();
21535                     e.preventDefault();
21536                     dateChanged = true;
21537                 }
21538                 break;
21539             case 13: // enter
21540                 this.setValue(this.formatDate(this.date));
21541                 this.hidePopup();
21542                 e.preventDefault();
21543                 break;
21544             case 9: // tab
21545                 this.setValue(this.formatDate(this.date));
21546                 this.hidePopup();
21547                 break;
21548             case 16: // shift
21549             case 17: // ctrl
21550             case 18: // alt
21551                 break;
21552             default :
21553                 this.hidePopup();
21554                 
21555         }
21556     },
21557     
21558     
21559     onClick: function(e) 
21560     {
21561         e.stopPropagation();
21562         e.preventDefault();
21563         
21564         var target = e.getTarget();
21565         
21566         if(target.nodeName.toLowerCase() === 'i'){
21567             target = Roo.get(target).dom.parentNode;
21568         }
21569         
21570         var nodeName = target.nodeName;
21571         var className = target.className;
21572         var html = target.innerHTML;
21573         //Roo.log(nodeName);
21574         
21575         switch(nodeName.toLowerCase()) {
21576             case 'th':
21577                 switch(className) {
21578                     case 'switch':
21579                         this.showMode(1);
21580                         break;
21581                     case 'prev':
21582                     case 'next':
21583                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21584                         switch(this.viewMode){
21585                                 case 0:
21586                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21587                                         break;
21588                                 case 1:
21589                                 case 2:
21590                                         this.viewDate = this.moveYear(this.viewDate, dir);
21591                                         break;
21592                         }
21593                         this.fill();
21594                         break;
21595                     case 'today':
21596                         var date = new Date();
21597                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21598 //                        this.fill()
21599                         this.setValue(this.formatDate(this.date));
21600                         
21601                         this.hidePopup();
21602                         break;
21603                 }
21604                 break;
21605             case 'span':
21606                 if (className.indexOf('disabled') < 0) {
21607                     this.viewDate.setUTCDate(1);
21608                     if (className.indexOf('month') > -1) {
21609                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21610                     } else {
21611                         var year = parseInt(html, 10) || 0;
21612                         this.viewDate.setUTCFullYear(year);
21613                         
21614                     }
21615                     
21616                     if(this.singleMode){
21617                         this.setValue(this.formatDate(this.viewDate));
21618                         this.hidePopup();
21619                         return;
21620                     }
21621                     
21622                     this.showMode(-1);
21623                     this.fill();
21624                 }
21625                 break;
21626                 
21627             case 'td':
21628                 //Roo.log(className);
21629                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21630                     var day = parseInt(html, 10) || 1;
21631                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21632                         month = (this.viewDate || new Date()).getUTCMonth();
21633
21634                     if (className.indexOf('old') > -1) {
21635                         if(month === 0 ){
21636                             month = 11;
21637                             year -= 1;
21638                         }else{
21639                             month -= 1;
21640                         }
21641                     } else if (className.indexOf('new') > -1) {
21642                         if (month == 11) {
21643                             month = 0;
21644                             year += 1;
21645                         } else {
21646                             month += 1;
21647                         }
21648                     }
21649                     //Roo.log([year,month,day]);
21650                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21651                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21652 //                    this.fill();
21653                     //Roo.log(this.formatDate(this.date));
21654                     this.setValue(this.formatDate(this.date));
21655                     this.hidePopup();
21656                 }
21657                 break;
21658         }
21659     },
21660     
21661     setStartDate: function(startDate)
21662     {
21663         this.startDate = startDate || -Infinity;
21664         if (this.startDate !== -Infinity) {
21665             this.startDate = this.parseDate(this.startDate);
21666         }
21667         this.update();
21668         this.updateNavArrows();
21669     },
21670
21671     setEndDate: function(endDate)
21672     {
21673         this.endDate = endDate || Infinity;
21674         if (this.endDate !== Infinity) {
21675             this.endDate = this.parseDate(this.endDate);
21676         }
21677         this.update();
21678         this.updateNavArrows();
21679     },
21680     
21681     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21682     {
21683         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21684         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21685             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21686         }
21687         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21688             return parseInt(d, 10);
21689         });
21690         this.update();
21691         this.updateNavArrows();
21692     },
21693     
21694     updateNavArrows: function() 
21695     {
21696         if(this.singleMode){
21697             return;
21698         }
21699         
21700         var d = new Date(this.viewDate),
21701         year = d.getUTCFullYear(),
21702         month = d.getUTCMonth();
21703         
21704         Roo.each(this.picker().select('.prev', true).elements, function(v){
21705             v.show();
21706             switch (this.viewMode) {
21707                 case 0:
21708
21709                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21710                         v.hide();
21711                     }
21712                     break;
21713                 case 1:
21714                 case 2:
21715                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21716                         v.hide();
21717                     }
21718                     break;
21719             }
21720         });
21721         
21722         Roo.each(this.picker().select('.next', true).elements, function(v){
21723             v.show();
21724             switch (this.viewMode) {
21725                 case 0:
21726
21727                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21728                         v.hide();
21729                     }
21730                     break;
21731                 case 1:
21732                 case 2:
21733                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21734                         v.hide();
21735                     }
21736                     break;
21737             }
21738         })
21739     },
21740     
21741     moveMonth: function(date, dir)
21742     {
21743         if (!dir) {
21744             return date;
21745         }
21746         var new_date = new Date(date.valueOf()),
21747         day = new_date.getUTCDate(),
21748         month = new_date.getUTCMonth(),
21749         mag = Math.abs(dir),
21750         new_month, test;
21751         dir = dir > 0 ? 1 : -1;
21752         if (mag == 1){
21753             test = dir == -1
21754             // If going back one month, make sure month is not current month
21755             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21756             ? function(){
21757                 return new_date.getUTCMonth() == month;
21758             }
21759             // If going forward one month, make sure month is as expected
21760             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21761             : function(){
21762                 return new_date.getUTCMonth() != new_month;
21763             };
21764             new_month = month + dir;
21765             new_date.setUTCMonth(new_month);
21766             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21767             if (new_month < 0 || new_month > 11) {
21768                 new_month = (new_month + 12) % 12;
21769             }
21770         } else {
21771             // For magnitudes >1, move one month at a time...
21772             for (var i=0; i<mag; i++) {
21773                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21774                 new_date = this.moveMonth(new_date, dir);
21775             }
21776             // ...then reset the day, keeping it in the new month
21777             new_month = new_date.getUTCMonth();
21778             new_date.setUTCDate(day);
21779             test = function(){
21780                 return new_month != new_date.getUTCMonth();
21781             };
21782         }
21783         // Common date-resetting loop -- if date is beyond end of month, make it
21784         // end of month
21785         while (test()){
21786             new_date.setUTCDate(--day);
21787             new_date.setUTCMonth(new_month);
21788         }
21789         return new_date;
21790     },
21791
21792     moveYear: function(date, dir)
21793     {
21794         return this.moveMonth(date, dir*12);
21795     },
21796
21797     dateWithinRange: function(date)
21798     {
21799         return date >= this.startDate && date <= this.endDate;
21800     },
21801
21802     
21803     remove: function() 
21804     {
21805         this.picker().remove();
21806     },
21807     
21808     validateValue : function(value)
21809     {
21810         if(this.getVisibilityEl().hasClass('hidden')){
21811             return true;
21812         }
21813         
21814         if(value.length < 1)  {
21815             if(this.allowBlank){
21816                 return true;
21817             }
21818             return false;
21819         }
21820         
21821         if(value.length < this.minLength){
21822             return false;
21823         }
21824         if(value.length > this.maxLength){
21825             return false;
21826         }
21827         if(this.vtype){
21828             var vt = Roo.form.VTypes;
21829             if(!vt[this.vtype](value, this)){
21830                 return false;
21831             }
21832         }
21833         if(typeof this.validator == "function"){
21834             var msg = this.validator(value);
21835             if(msg !== true){
21836                 return false;
21837             }
21838         }
21839         
21840         if(this.regex && !this.regex.test(value)){
21841             return false;
21842         }
21843         
21844         if(typeof(this.parseDate(value)) == 'undefined'){
21845             return false;
21846         }
21847         
21848         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21849             return false;
21850         }      
21851         
21852         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21853             return false;
21854         } 
21855         
21856         
21857         return true;
21858     },
21859     
21860     reset : function()
21861     {
21862         this.date = this.viewDate = '';
21863         
21864         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21865     }
21866    
21867 });
21868
21869 Roo.apply(Roo.bootstrap.DateField,  {
21870     
21871     head : {
21872         tag: 'thead',
21873         cn: [
21874         {
21875             tag: 'tr',
21876             cn: [
21877             {
21878                 tag: 'th',
21879                 cls: 'prev',
21880                 html: '<i class="fa fa-arrow-left"/>'
21881             },
21882             {
21883                 tag: 'th',
21884                 cls: 'switch',
21885                 colspan: '5'
21886             },
21887             {
21888                 tag: 'th',
21889                 cls: 'next',
21890                 html: '<i class="fa fa-arrow-right"/>'
21891             }
21892
21893             ]
21894         }
21895         ]
21896     },
21897     
21898     content : {
21899         tag: 'tbody',
21900         cn: [
21901         {
21902             tag: 'tr',
21903             cn: [
21904             {
21905                 tag: 'td',
21906                 colspan: '7'
21907             }
21908             ]
21909         }
21910         ]
21911     },
21912     
21913     footer : {
21914         tag: 'tfoot',
21915         cn: [
21916         {
21917             tag: 'tr',
21918             cn: [
21919             {
21920                 tag: 'th',
21921                 colspan: '7',
21922                 cls: 'today'
21923             }
21924                     
21925             ]
21926         }
21927         ]
21928     },
21929     
21930     dates:{
21931         en: {
21932             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21933             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21934             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21935             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21936             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21937             today: "Today"
21938         }
21939     },
21940     
21941     modes: [
21942     {
21943         clsName: 'days',
21944         navFnc: 'Month',
21945         navStep: 1
21946     },
21947     {
21948         clsName: 'months',
21949         navFnc: 'FullYear',
21950         navStep: 1
21951     },
21952     {
21953         clsName: 'years',
21954         navFnc: 'FullYear',
21955         navStep: 10
21956     }]
21957 });
21958
21959 Roo.apply(Roo.bootstrap.DateField,  {
21960   
21961     template : {
21962         tag: 'div',
21963         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21964         cn: [
21965         {
21966             tag: 'div',
21967             cls: 'datepicker-days',
21968             cn: [
21969             {
21970                 tag: 'table',
21971                 cls: 'table-condensed',
21972                 cn:[
21973                 Roo.bootstrap.DateField.head,
21974                 {
21975                     tag: 'tbody'
21976                 },
21977                 Roo.bootstrap.DateField.footer
21978                 ]
21979             }
21980             ]
21981         },
21982         {
21983             tag: 'div',
21984             cls: 'datepicker-months',
21985             cn: [
21986             {
21987                 tag: 'table',
21988                 cls: 'table-condensed',
21989                 cn:[
21990                 Roo.bootstrap.DateField.head,
21991                 Roo.bootstrap.DateField.content,
21992                 Roo.bootstrap.DateField.footer
21993                 ]
21994             }
21995             ]
21996         },
21997         {
21998             tag: 'div',
21999             cls: 'datepicker-years',
22000             cn: [
22001             {
22002                 tag: 'table',
22003                 cls: 'table-condensed',
22004                 cn:[
22005                 Roo.bootstrap.DateField.head,
22006                 Roo.bootstrap.DateField.content,
22007                 Roo.bootstrap.DateField.footer
22008                 ]
22009             }
22010             ]
22011         }
22012         ]
22013     }
22014 });
22015
22016  
22017
22018  /*
22019  * - LGPL
22020  *
22021  * TimeField
22022  * 
22023  */
22024
22025 /**
22026  * @class Roo.bootstrap.TimeField
22027  * @extends Roo.bootstrap.Input
22028  * Bootstrap DateField class
22029  * 
22030  * 
22031  * @constructor
22032  * Create a new TimeField
22033  * @param {Object} config The config object
22034  */
22035
22036 Roo.bootstrap.TimeField = function(config){
22037     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22038     this.addEvents({
22039             /**
22040              * @event show
22041              * Fires when this field show.
22042              * @param {Roo.bootstrap.DateField} thisthis
22043              * @param {Mixed} date The date value
22044              */
22045             show : true,
22046             /**
22047              * @event show
22048              * Fires when this field hide.
22049              * @param {Roo.bootstrap.DateField} this
22050              * @param {Mixed} date The date value
22051              */
22052             hide : true,
22053             /**
22054              * @event select
22055              * Fires when select a date.
22056              * @param {Roo.bootstrap.DateField} this
22057              * @param {Mixed} date The date value
22058              */
22059             select : true
22060         });
22061 };
22062
22063 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22064     
22065     /**
22066      * @cfg {String} format
22067      * The default time format string which can be overriden for localization support.  The format must be
22068      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22069      */
22070     format : "H:i",
22071
22072     getAutoCreate : function()
22073     {
22074         this.after = '<i class="fa far fa-clock"></i>';
22075         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22076         
22077          
22078     },
22079     onRender: function(ct, position)
22080     {
22081         
22082         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22083                 
22084         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22085         
22086         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22087         
22088         this.pop = this.picker().select('>.datepicker-time',true).first();
22089         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22090         
22091         this.picker().on('mousedown', this.onMousedown, this);
22092         this.picker().on('click', this.onClick, this);
22093         
22094         this.picker().addClass('datepicker-dropdown');
22095     
22096         this.fillTime();
22097         this.update();
22098             
22099         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22100         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22101         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22102         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22103         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22104         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22105
22106     },
22107     
22108     fireKey: function(e){
22109         if (!this.picker().isVisible()){
22110             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22111                 this.show();
22112             }
22113             return;
22114         }
22115
22116         e.preventDefault();
22117         
22118         switch(e.keyCode){
22119             case 27: // escape
22120                 this.hide();
22121                 break;
22122             case 37: // left
22123             case 39: // right
22124                 this.onTogglePeriod();
22125                 break;
22126             case 38: // up
22127                 this.onIncrementMinutes();
22128                 break;
22129             case 40: // down
22130                 this.onDecrementMinutes();
22131                 break;
22132             case 13: // enter
22133             case 9: // tab
22134                 this.setTime();
22135                 break;
22136         }
22137     },
22138     
22139     onClick: function(e) {
22140         e.stopPropagation();
22141         e.preventDefault();
22142     },
22143     
22144     picker : function()
22145     {
22146         return this.pickerEl;
22147     },
22148     
22149     fillTime: function()
22150     {    
22151         var time = this.pop.select('tbody', true).first();
22152         
22153         time.dom.innerHTML = '';
22154         
22155         time.createChild({
22156             tag: 'tr',
22157             cn: [
22158                 {
22159                     tag: 'td',
22160                     cn: [
22161                         {
22162                             tag: 'a',
22163                             href: '#',
22164                             cls: 'btn',
22165                             cn: [
22166                                 {
22167                                     tag: 'i',
22168                                     cls: 'hours-up fa fas fa-chevron-up'
22169                                 }
22170                             ]
22171                         } 
22172                     ]
22173                 },
22174                 {
22175                     tag: 'td',
22176                     cls: 'separator'
22177                 },
22178                 {
22179                     tag: 'td',
22180                     cn: [
22181                         {
22182                             tag: 'a',
22183                             href: '#',
22184                             cls: 'btn',
22185                             cn: [
22186                                 {
22187                                     tag: 'i',
22188                                     cls: 'minutes-up fa fas fa-chevron-up'
22189                                 }
22190                             ]
22191                         }
22192                     ]
22193                 },
22194                 {
22195                     tag: 'td',
22196                     cls: 'separator'
22197                 }
22198             ]
22199         });
22200         
22201         time.createChild({
22202             tag: 'tr',
22203             cn: [
22204                 {
22205                     tag: 'td',
22206                     cn: [
22207                         {
22208                             tag: 'span',
22209                             cls: 'timepicker-hour',
22210                             html: '00'
22211                         }  
22212                     ]
22213                 },
22214                 {
22215                     tag: 'td',
22216                     cls: 'separator',
22217                     html: ':'
22218                 },
22219                 {
22220                     tag: 'td',
22221                     cn: [
22222                         {
22223                             tag: 'span',
22224                             cls: 'timepicker-minute',
22225                             html: '00'
22226                         }  
22227                     ]
22228                 },
22229                 {
22230                     tag: 'td',
22231                     cls: 'separator'
22232                 },
22233                 {
22234                     tag: 'td',
22235                     cn: [
22236                         {
22237                             tag: 'button',
22238                             type: 'button',
22239                             cls: 'btn btn-primary period',
22240                             html: 'AM'
22241                             
22242                         }
22243                     ]
22244                 }
22245             ]
22246         });
22247         
22248         time.createChild({
22249             tag: 'tr',
22250             cn: [
22251                 {
22252                     tag: 'td',
22253                     cn: [
22254                         {
22255                             tag: 'a',
22256                             href: '#',
22257                             cls: 'btn',
22258                             cn: [
22259                                 {
22260                                     tag: 'span',
22261                                     cls: 'hours-down fa fas fa-chevron-down'
22262                                 }
22263                             ]
22264                         }
22265                     ]
22266                 },
22267                 {
22268                     tag: 'td',
22269                     cls: 'separator'
22270                 },
22271                 {
22272                     tag: 'td',
22273                     cn: [
22274                         {
22275                             tag: 'a',
22276                             href: '#',
22277                             cls: 'btn',
22278                             cn: [
22279                                 {
22280                                     tag: 'span',
22281                                     cls: 'minutes-down fa fas fa-chevron-down'
22282                                 }
22283                             ]
22284                         }
22285                     ]
22286                 },
22287                 {
22288                     tag: 'td',
22289                     cls: 'separator'
22290                 }
22291             ]
22292         });
22293         
22294     },
22295     
22296     update: function()
22297     {
22298         
22299         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22300         
22301         this.fill();
22302     },
22303     
22304     fill: function() 
22305     {
22306         var hours = this.time.getHours();
22307         var minutes = this.time.getMinutes();
22308         var period = 'AM';
22309         
22310         if(hours > 11){
22311             period = 'PM';
22312         }
22313         
22314         if(hours == 0){
22315             hours = 12;
22316         }
22317         
22318         
22319         if(hours > 12){
22320             hours = hours - 12;
22321         }
22322         
22323         if(hours < 10){
22324             hours = '0' + hours;
22325         }
22326         
22327         if(minutes < 10){
22328             minutes = '0' + minutes;
22329         }
22330         
22331         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22332         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22333         this.pop.select('button', true).first().dom.innerHTML = period;
22334         
22335     },
22336     
22337     place: function()
22338     {   
22339         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22340         
22341         var cls = ['bottom'];
22342         
22343         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22344             cls.pop();
22345             cls.push('top');
22346         }
22347         
22348         cls.push('right');
22349         
22350         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22351             cls.pop();
22352             cls.push('left');
22353         }
22354         //this.picker().setXY(20000,20000);
22355         this.picker().addClass(cls.join('-'));
22356         
22357         var _this = this;
22358         
22359         Roo.each(cls, function(c){
22360             if(c == 'bottom'){
22361                 (function() {
22362                  //  
22363                 }).defer(200);
22364                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22365                 //_this.picker().setTop(_this.inputEl().getHeight());
22366                 return;
22367             }
22368             if(c == 'top'){
22369                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22370                 
22371                 //_this.picker().setTop(0 - _this.picker().getHeight());
22372                 return;
22373             }
22374             /*
22375             if(c == 'left'){
22376                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22377                 return;
22378             }
22379             if(c == 'right'){
22380                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22381                 return;
22382             }
22383             */
22384         });
22385         
22386     },
22387   
22388     onFocus : function()
22389     {
22390         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22391         this.show();
22392     },
22393     
22394     onBlur : function()
22395     {
22396         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22397         this.hide();
22398     },
22399     
22400     show : function()
22401     {
22402         this.picker().show();
22403         this.pop.show();
22404         this.update();
22405         this.place();
22406         
22407         this.fireEvent('show', this, this.date);
22408     },
22409     
22410     hide : function()
22411     {
22412         this.picker().hide();
22413         this.pop.hide();
22414         
22415         this.fireEvent('hide', this, this.date);
22416     },
22417     
22418     setTime : function()
22419     {
22420         this.hide();
22421         this.setValue(this.time.format(this.format));
22422         
22423         this.fireEvent('select', this, this.date);
22424         
22425         
22426     },
22427     
22428     onMousedown: function(e){
22429         e.stopPropagation();
22430         e.preventDefault();
22431     },
22432     
22433     onIncrementHours: function()
22434     {
22435         Roo.log('onIncrementHours');
22436         this.time = this.time.add(Date.HOUR, 1);
22437         this.update();
22438         
22439     },
22440     
22441     onDecrementHours: function()
22442     {
22443         Roo.log('onDecrementHours');
22444         this.time = this.time.add(Date.HOUR, -1);
22445         this.update();
22446     },
22447     
22448     onIncrementMinutes: function()
22449     {
22450         Roo.log('onIncrementMinutes');
22451         this.time = this.time.add(Date.MINUTE, 1);
22452         this.update();
22453     },
22454     
22455     onDecrementMinutes: function()
22456     {
22457         Roo.log('onDecrementMinutes');
22458         this.time = this.time.add(Date.MINUTE, -1);
22459         this.update();
22460     },
22461     
22462     onTogglePeriod: function()
22463     {
22464         Roo.log('onTogglePeriod');
22465         this.time = this.time.add(Date.HOUR, 12);
22466         this.update();
22467     }
22468     
22469    
22470 });
22471  
22472
22473 Roo.apply(Roo.bootstrap.TimeField,  {
22474   
22475     template : {
22476         tag: 'div',
22477         cls: 'datepicker dropdown-menu',
22478         cn: [
22479             {
22480                 tag: 'div',
22481                 cls: 'datepicker-time',
22482                 cn: [
22483                 {
22484                     tag: 'table',
22485                     cls: 'table-condensed',
22486                     cn:[
22487                         {
22488                             tag: 'tbody',
22489                             cn: [
22490                                 {
22491                                     tag: 'tr',
22492                                     cn: [
22493                                     {
22494                                         tag: 'td',
22495                                         colspan: '7'
22496                                     }
22497                                     ]
22498                                 }
22499                             ]
22500                         },
22501                         {
22502                             tag: 'tfoot',
22503                             cn: [
22504                                 {
22505                                     tag: 'tr',
22506                                     cn: [
22507                                     {
22508                                         tag: 'th',
22509                                         colspan: '7',
22510                                         cls: '',
22511                                         cn: [
22512                                             {
22513                                                 tag: 'button',
22514                                                 cls: 'btn btn-info ok',
22515                                                 html: 'OK'
22516                                             }
22517                                         ]
22518                                     }
22519                     
22520                                     ]
22521                                 }
22522                             ]
22523                         }
22524                     ]
22525                 }
22526                 ]
22527             }
22528         ]
22529     }
22530 });
22531
22532  
22533
22534  /*
22535  * - LGPL
22536  *
22537  * MonthField
22538  * 
22539  */
22540
22541 /**
22542  * @class Roo.bootstrap.MonthField
22543  * @extends Roo.bootstrap.Input
22544  * Bootstrap MonthField class
22545  * 
22546  * @cfg {String} language default en
22547  * 
22548  * @constructor
22549  * Create a new MonthField
22550  * @param {Object} config The config object
22551  */
22552
22553 Roo.bootstrap.MonthField = function(config){
22554     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22555     
22556     this.addEvents({
22557         /**
22558          * @event show
22559          * Fires when this field show.
22560          * @param {Roo.bootstrap.MonthField} this
22561          * @param {Mixed} date The date value
22562          */
22563         show : true,
22564         /**
22565          * @event show
22566          * Fires when this field hide.
22567          * @param {Roo.bootstrap.MonthField} this
22568          * @param {Mixed} date The date value
22569          */
22570         hide : true,
22571         /**
22572          * @event select
22573          * Fires when select a date.
22574          * @param {Roo.bootstrap.MonthField} this
22575          * @param {String} oldvalue The old value
22576          * @param {String} newvalue The new value
22577          */
22578         select : true
22579     });
22580 };
22581
22582 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22583     
22584     onRender: function(ct, position)
22585     {
22586         
22587         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22588         
22589         this.language = this.language || 'en';
22590         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22591         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22592         
22593         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22594         this.isInline = false;
22595         this.isInput = true;
22596         this.component = this.el.select('.add-on', true).first() || false;
22597         this.component = (this.component && this.component.length === 0) ? false : this.component;
22598         this.hasInput = this.component && this.inputEL().length;
22599         
22600         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22601         
22602         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22603         
22604         this.picker().on('mousedown', this.onMousedown, this);
22605         this.picker().on('click', this.onClick, this);
22606         
22607         this.picker().addClass('datepicker-dropdown');
22608         
22609         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22610             v.setStyle('width', '189px');
22611         });
22612         
22613         this.fillMonths();
22614         
22615         this.update();
22616         
22617         if(this.isInline) {
22618             this.show();
22619         }
22620         
22621     },
22622     
22623     setValue: function(v, suppressEvent)
22624     {   
22625         var o = this.getValue();
22626         
22627         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22628         
22629         this.update();
22630
22631         if(suppressEvent !== true){
22632             this.fireEvent('select', this, o, v);
22633         }
22634         
22635     },
22636     
22637     getValue: function()
22638     {
22639         return this.value;
22640     },
22641     
22642     onClick: function(e) 
22643     {
22644         e.stopPropagation();
22645         e.preventDefault();
22646         
22647         var target = e.getTarget();
22648         
22649         if(target.nodeName.toLowerCase() === 'i'){
22650             target = Roo.get(target).dom.parentNode;
22651         }
22652         
22653         var nodeName = target.nodeName;
22654         var className = target.className;
22655         var html = target.innerHTML;
22656         
22657         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22658             return;
22659         }
22660         
22661         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22662         
22663         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22664         
22665         this.hide();
22666                         
22667     },
22668     
22669     picker : function()
22670     {
22671         return this.pickerEl;
22672     },
22673     
22674     fillMonths: function()
22675     {    
22676         var i = 0;
22677         var months = this.picker().select('>.datepicker-months td', true).first();
22678         
22679         months.dom.innerHTML = '';
22680         
22681         while (i < 12) {
22682             var month = {
22683                 tag: 'span',
22684                 cls: 'month',
22685                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22686             };
22687             
22688             months.createChild(month);
22689         }
22690         
22691     },
22692     
22693     update: function()
22694     {
22695         var _this = this;
22696         
22697         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22698             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22699         }
22700         
22701         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22702             e.removeClass('active');
22703             
22704             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22705                 e.addClass('active');
22706             }
22707         })
22708     },
22709     
22710     place: function()
22711     {
22712         if(this.isInline) {
22713             return;
22714         }
22715         
22716         this.picker().removeClass(['bottom', 'top']);
22717         
22718         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22719             /*
22720              * place to the top of element!
22721              *
22722              */
22723             
22724             this.picker().addClass('top');
22725             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22726             
22727             return;
22728         }
22729         
22730         this.picker().addClass('bottom');
22731         
22732         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22733     },
22734     
22735     onFocus : function()
22736     {
22737         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22738         this.show();
22739     },
22740     
22741     onBlur : function()
22742     {
22743         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22744         
22745         var d = this.inputEl().getValue();
22746         
22747         this.setValue(d);
22748                 
22749         this.hide();
22750     },
22751     
22752     show : function()
22753     {
22754         this.picker().show();
22755         this.picker().select('>.datepicker-months', true).first().show();
22756         this.update();
22757         this.place();
22758         
22759         this.fireEvent('show', this, this.date);
22760     },
22761     
22762     hide : function()
22763     {
22764         if(this.isInline) {
22765             return;
22766         }
22767         this.picker().hide();
22768         this.fireEvent('hide', this, this.date);
22769         
22770     },
22771     
22772     onMousedown: function(e)
22773     {
22774         e.stopPropagation();
22775         e.preventDefault();
22776     },
22777     
22778     keyup: function(e)
22779     {
22780         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22781         this.update();
22782     },
22783
22784     fireKey: function(e)
22785     {
22786         if (!this.picker().isVisible()){
22787             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22788                 this.show();
22789             }
22790             return;
22791         }
22792         
22793         var dir;
22794         
22795         switch(e.keyCode){
22796             case 27: // escape
22797                 this.hide();
22798                 e.preventDefault();
22799                 break;
22800             case 37: // left
22801             case 39: // right
22802                 dir = e.keyCode == 37 ? -1 : 1;
22803                 
22804                 this.vIndex = this.vIndex + dir;
22805                 
22806                 if(this.vIndex < 0){
22807                     this.vIndex = 0;
22808                 }
22809                 
22810                 if(this.vIndex > 11){
22811                     this.vIndex = 11;
22812                 }
22813                 
22814                 if(isNaN(this.vIndex)){
22815                     this.vIndex = 0;
22816                 }
22817                 
22818                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22819                 
22820                 break;
22821             case 38: // up
22822             case 40: // down
22823                 
22824                 dir = e.keyCode == 38 ? -1 : 1;
22825                 
22826                 this.vIndex = this.vIndex + dir * 4;
22827                 
22828                 if(this.vIndex < 0){
22829                     this.vIndex = 0;
22830                 }
22831                 
22832                 if(this.vIndex > 11){
22833                     this.vIndex = 11;
22834                 }
22835                 
22836                 if(isNaN(this.vIndex)){
22837                     this.vIndex = 0;
22838                 }
22839                 
22840                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22841                 break;
22842                 
22843             case 13: // enter
22844                 
22845                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22846                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22847                 }
22848                 
22849                 this.hide();
22850                 e.preventDefault();
22851                 break;
22852             case 9: // tab
22853                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22854                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22855                 }
22856                 this.hide();
22857                 break;
22858             case 16: // shift
22859             case 17: // ctrl
22860             case 18: // alt
22861                 break;
22862             default :
22863                 this.hide();
22864                 
22865         }
22866     },
22867     
22868     remove: function() 
22869     {
22870         this.picker().remove();
22871     }
22872    
22873 });
22874
22875 Roo.apply(Roo.bootstrap.MonthField,  {
22876     
22877     content : {
22878         tag: 'tbody',
22879         cn: [
22880         {
22881             tag: 'tr',
22882             cn: [
22883             {
22884                 tag: 'td',
22885                 colspan: '7'
22886             }
22887             ]
22888         }
22889         ]
22890     },
22891     
22892     dates:{
22893         en: {
22894             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22895             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22896         }
22897     }
22898 });
22899
22900 Roo.apply(Roo.bootstrap.MonthField,  {
22901   
22902     template : {
22903         tag: 'div',
22904         cls: 'datepicker dropdown-menu roo-dynamic',
22905         cn: [
22906             {
22907                 tag: 'div',
22908                 cls: 'datepicker-months',
22909                 cn: [
22910                 {
22911                     tag: 'table',
22912                     cls: 'table-condensed',
22913                     cn:[
22914                         Roo.bootstrap.DateField.content
22915                     ]
22916                 }
22917                 ]
22918             }
22919         ]
22920     }
22921 });
22922
22923  
22924
22925  
22926  /*
22927  * - LGPL
22928  *
22929  * CheckBox
22930  * 
22931  */
22932
22933 /**
22934  * @class Roo.bootstrap.CheckBox
22935  * @extends Roo.bootstrap.Input
22936  * Bootstrap CheckBox class
22937  * 
22938  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22939  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22940  * @cfg {String} boxLabel The text that appears beside the checkbox
22941  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22942  * @cfg {Boolean} checked initnal the element
22943  * @cfg {Boolean} inline inline the element (default false)
22944  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22945  * @cfg {String} tooltip label tooltip
22946  * 
22947  * @constructor
22948  * Create a new CheckBox
22949  * @param {Object} config The config object
22950  */
22951
22952 Roo.bootstrap.CheckBox = function(config){
22953     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22954    
22955     this.addEvents({
22956         /**
22957         * @event check
22958         * Fires when the element is checked or unchecked.
22959         * @param {Roo.bootstrap.CheckBox} this This input
22960         * @param {Boolean} checked The new checked value
22961         */
22962        check : true,
22963        /**
22964         * @event click
22965         * Fires when the element is click.
22966         * @param {Roo.bootstrap.CheckBox} this This input
22967         */
22968        click : true
22969     });
22970     
22971 };
22972
22973 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22974   
22975     inputType: 'checkbox',
22976     inputValue: 1,
22977     valueOff: 0,
22978     boxLabel: false,
22979     checked: false,
22980     weight : false,
22981     inline: false,
22982     tooltip : '',
22983     
22984     // checkbox success does not make any sense really.. 
22985     invalidClass : "",
22986     validClass : "",
22987     
22988     
22989     getAutoCreate : function()
22990     {
22991         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22992         
22993         var id = Roo.id();
22994         
22995         var cfg = {};
22996         
22997         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22998         
22999         if(this.inline){
23000             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
23001         }
23002         
23003         var input =  {
23004             tag: 'input',
23005             id : id,
23006             type : this.inputType,
23007             value : this.inputValue,
23008             cls : 'roo-' + this.inputType, //'form-box',
23009             placeholder : this.placeholder || ''
23010             
23011         };
23012         
23013         if(this.inputType != 'radio'){
23014             var hidden =  {
23015                 tag: 'input',
23016                 type : 'hidden',
23017                 cls : 'roo-hidden-value',
23018                 value : this.checked ? this.inputValue : this.valueOff
23019             };
23020         }
23021         
23022             
23023         if (this.weight) { // Validity check?
23024             cfg.cls += " " + this.inputType + "-" + this.weight;
23025         }
23026         
23027         if (this.disabled) {
23028             input.disabled=true;
23029         }
23030         
23031         if(this.checked){
23032             input.checked = this.checked;
23033         }
23034         
23035         if (this.name) {
23036             
23037             input.name = this.name;
23038             
23039             if(this.inputType != 'radio'){
23040                 hidden.name = this.name;
23041                 input.name = '_hidden_' + this.name;
23042             }
23043         }
23044         
23045         if (this.size) {
23046             input.cls += ' input-' + this.size;
23047         }
23048         
23049         var settings=this;
23050         
23051         ['xs','sm','md','lg'].map(function(size){
23052             if (settings[size]) {
23053                 cfg.cls += ' col-' + size + '-' + settings[size];
23054             }
23055         });
23056         
23057         var inputblock = input;
23058          
23059         if (this.before || this.after) {
23060             
23061             inputblock = {
23062                 cls : 'input-group',
23063                 cn :  [] 
23064             };
23065             
23066             if (this.before) {
23067                 inputblock.cn.push({
23068                     tag :'span',
23069                     cls : 'input-group-addon',
23070                     html : this.before
23071                 });
23072             }
23073             
23074             inputblock.cn.push(input);
23075             
23076             if(this.inputType != 'radio'){
23077                 inputblock.cn.push(hidden);
23078             }
23079             
23080             if (this.after) {
23081                 inputblock.cn.push({
23082                     tag :'span',
23083                     cls : 'input-group-addon',
23084                     html : this.after
23085                 });
23086             }
23087             
23088         }
23089         var boxLabelCfg = false;
23090         
23091         if(this.boxLabel){
23092            
23093             boxLabelCfg = {
23094                 tag: 'label',
23095                 //'for': id, // box label is handled by onclick - so no for...
23096                 cls: 'box-label',
23097                 html: this.boxLabel
23098             };
23099             if(this.tooltip){
23100                 boxLabelCfg.tooltip = this.tooltip;
23101             }
23102              
23103         }
23104         
23105         
23106         if (align ==='left' && this.fieldLabel.length) {
23107 //                Roo.log("left and has label");
23108             cfg.cn = [
23109                 {
23110                     tag: 'label',
23111                     'for' :  id,
23112                     cls : 'control-label',
23113                     html : this.fieldLabel
23114                 },
23115                 {
23116                     cls : "", 
23117                     cn: [
23118                         inputblock
23119                     ]
23120                 }
23121             ];
23122             
23123             if (boxLabelCfg) {
23124                 cfg.cn[1].cn.push(boxLabelCfg);
23125             }
23126             
23127             if(this.labelWidth > 12){
23128                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23129             }
23130             
23131             if(this.labelWidth < 13 && this.labelmd == 0){
23132                 this.labelmd = this.labelWidth;
23133             }
23134             
23135             if(this.labellg > 0){
23136                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23137                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23138             }
23139             
23140             if(this.labelmd > 0){
23141                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23142                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23143             }
23144             
23145             if(this.labelsm > 0){
23146                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23147                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23148             }
23149             
23150             if(this.labelxs > 0){
23151                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23152                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23153             }
23154             
23155         } else if ( this.fieldLabel.length) {
23156 //                Roo.log(" label");
23157                 cfg.cn = [
23158                    
23159                     {
23160                         tag: this.boxLabel ? 'span' : 'label',
23161                         'for': id,
23162                         cls: 'control-label box-input-label',
23163                         //cls : 'input-group-addon',
23164                         html : this.fieldLabel
23165                     },
23166                     
23167                     inputblock
23168                     
23169                 ];
23170                 if (boxLabelCfg) {
23171                     cfg.cn.push(boxLabelCfg);
23172                 }
23173
23174         } else {
23175             
23176 //                Roo.log(" no label && no align");
23177                 cfg.cn = [  inputblock ] ;
23178                 if (boxLabelCfg) {
23179                     cfg.cn.push(boxLabelCfg);
23180                 }
23181
23182                 
23183         }
23184         
23185        
23186         
23187         if(this.inputType != 'radio'){
23188             cfg.cn.push(hidden);
23189         }
23190         
23191         return cfg;
23192         
23193     },
23194     
23195     /**
23196      * return the real input element.
23197      */
23198     inputEl: function ()
23199     {
23200         return this.el.select('input.roo-' + this.inputType,true).first();
23201     },
23202     hiddenEl: function ()
23203     {
23204         return this.el.select('input.roo-hidden-value',true).first();
23205     },
23206     
23207     labelEl: function()
23208     {
23209         return this.el.select('label.control-label',true).first();
23210     },
23211     /* depricated... */
23212     
23213     label: function()
23214     {
23215         return this.labelEl();
23216     },
23217     
23218     boxLabelEl: function()
23219     {
23220         return this.el.select('label.box-label',true).first();
23221     },
23222     
23223     initEvents : function()
23224     {
23225 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23226         
23227         this.inputEl().on('click', this.onClick,  this);
23228         
23229         if (this.boxLabel) { 
23230             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23231         }
23232         
23233         this.startValue = this.getValue();
23234         
23235         if(this.groupId){
23236             Roo.bootstrap.CheckBox.register(this);
23237         }
23238     },
23239     
23240     onClick : function(e)
23241     {   
23242         if(this.fireEvent('click', this, e) !== false){
23243             this.setChecked(!this.checked);
23244         }
23245         
23246     },
23247     
23248     setChecked : function(state,suppressEvent)
23249     {
23250         this.startValue = this.getValue();
23251
23252         if(this.inputType == 'radio'){
23253             
23254             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23255                 e.dom.checked = false;
23256             });
23257             
23258             this.inputEl().dom.checked = true;
23259             
23260             this.inputEl().dom.value = this.inputValue;
23261             
23262             if(suppressEvent !== true){
23263                 this.fireEvent('check', this, true);
23264             }
23265             
23266             this.validate();
23267             
23268             return;
23269         }
23270         
23271         this.checked = state;
23272         
23273         this.inputEl().dom.checked = state;
23274         
23275         
23276         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23277         
23278         if(suppressEvent !== true){
23279             this.fireEvent('check', this, state);
23280         }
23281         
23282         this.validate();
23283     },
23284     
23285     getValue : function()
23286     {
23287         if(this.inputType == 'radio'){
23288             return this.getGroupValue();
23289         }
23290         
23291         return this.hiddenEl().dom.value;
23292         
23293     },
23294     
23295     getGroupValue : function()
23296     {
23297         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23298             return '';
23299         }
23300         
23301         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23302     },
23303     
23304     setValue : function(v,suppressEvent)
23305     {
23306         if(this.inputType == 'radio'){
23307             this.setGroupValue(v, suppressEvent);
23308             return;
23309         }
23310         
23311         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23312         
23313         this.validate();
23314     },
23315     
23316     setGroupValue : function(v, suppressEvent)
23317     {
23318         this.startValue = this.getValue();
23319         
23320         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23321             e.dom.checked = false;
23322             
23323             if(e.dom.value == v){
23324                 e.dom.checked = true;
23325             }
23326         });
23327         
23328         if(suppressEvent !== true){
23329             this.fireEvent('check', this, true);
23330         }
23331
23332         this.validate();
23333         
23334         return;
23335     },
23336     
23337     validate : function()
23338     {
23339         if(this.getVisibilityEl().hasClass('hidden')){
23340             return true;
23341         }
23342         
23343         if(
23344                 this.disabled || 
23345                 (this.inputType == 'radio' && this.validateRadio()) ||
23346                 (this.inputType == 'checkbox' && this.validateCheckbox())
23347         ){
23348             this.markValid();
23349             return true;
23350         }
23351         
23352         this.markInvalid();
23353         return false;
23354     },
23355     
23356     validateRadio : function()
23357     {
23358         if(this.getVisibilityEl().hasClass('hidden')){
23359             return true;
23360         }
23361         
23362         if(this.allowBlank){
23363             return true;
23364         }
23365         
23366         var valid = false;
23367         
23368         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23369             if(!e.dom.checked){
23370                 return;
23371             }
23372             
23373             valid = true;
23374             
23375             return false;
23376         });
23377         
23378         return valid;
23379     },
23380     
23381     validateCheckbox : function()
23382     {
23383         if(!this.groupId){
23384             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23385             //return (this.getValue() == this.inputValue) ? true : false;
23386         }
23387         
23388         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23389         
23390         if(!group){
23391             return false;
23392         }
23393         
23394         var r = false;
23395         
23396         for(var i in group){
23397             if(group[i].el.isVisible(true)){
23398                 r = false;
23399                 break;
23400             }
23401             
23402             r = true;
23403         }
23404         
23405         for(var i in group){
23406             if(r){
23407                 break;
23408             }
23409             
23410             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23411         }
23412         
23413         return r;
23414     },
23415     
23416     /**
23417      * Mark this field as valid
23418      */
23419     markValid : function()
23420     {
23421         var _this = this;
23422         
23423         this.fireEvent('valid', this);
23424         
23425         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23426         
23427         if(this.groupId){
23428             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23429         }
23430         
23431         if(label){
23432             label.markValid();
23433         }
23434
23435         if(this.inputType == 'radio'){
23436             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23437                 var fg = e.findParent('.form-group', false, true);
23438                 if (Roo.bootstrap.version == 3) {
23439                     fg.removeClass([_this.invalidClass, _this.validClass]);
23440                     fg.addClass(_this.validClass);
23441                 } else {
23442                     fg.removeClass(['is-valid', 'is-invalid']);
23443                     fg.addClass('is-valid');
23444                 }
23445             });
23446             
23447             return;
23448         }
23449
23450         if(!this.groupId){
23451             var fg = this.el.findParent('.form-group', false, true);
23452             if (Roo.bootstrap.version == 3) {
23453                 fg.removeClass([this.invalidClass, this.validClass]);
23454                 fg.addClass(this.validClass);
23455             } else {
23456                 fg.removeClass(['is-valid', 'is-invalid']);
23457                 fg.addClass('is-valid');
23458             }
23459             return;
23460         }
23461         
23462         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23463         
23464         if(!group){
23465             return;
23466         }
23467         
23468         for(var i in group){
23469             var fg = group[i].el.findParent('.form-group', false, true);
23470             if (Roo.bootstrap.version == 3) {
23471                 fg.removeClass([this.invalidClass, this.validClass]);
23472                 fg.addClass(this.validClass);
23473             } else {
23474                 fg.removeClass(['is-valid', 'is-invalid']);
23475                 fg.addClass('is-valid');
23476             }
23477         }
23478     },
23479     
23480      /**
23481      * Mark this field as invalid
23482      * @param {String} msg The validation message
23483      */
23484     markInvalid : function(msg)
23485     {
23486         if(this.allowBlank){
23487             return;
23488         }
23489         
23490         var _this = this;
23491         
23492         this.fireEvent('invalid', this, msg);
23493         
23494         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23495         
23496         if(this.groupId){
23497             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23498         }
23499         
23500         if(label){
23501             label.markInvalid();
23502         }
23503             
23504         if(this.inputType == 'radio'){
23505             
23506             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23507                 var fg = e.findParent('.form-group', false, true);
23508                 if (Roo.bootstrap.version == 3) {
23509                     fg.removeClass([_this.invalidClass, _this.validClass]);
23510                     fg.addClass(_this.invalidClass);
23511                 } else {
23512                     fg.removeClass(['is-invalid', 'is-valid']);
23513                     fg.addClass('is-invalid');
23514                 }
23515             });
23516             
23517             return;
23518         }
23519         
23520         if(!this.groupId){
23521             var fg = this.el.findParent('.form-group', false, true);
23522             if (Roo.bootstrap.version == 3) {
23523                 fg.removeClass([_this.invalidClass, _this.validClass]);
23524                 fg.addClass(_this.invalidClass);
23525             } else {
23526                 fg.removeClass(['is-invalid', 'is-valid']);
23527                 fg.addClass('is-invalid');
23528             }
23529             return;
23530         }
23531         
23532         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23533         
23534         if(!group){
23535             return;
23536         }
23537         
23538         for(var i in group){
23539             var fg = group[i].el.findParent('.form-group', false, true);
23540             if (Roo.bootstrap.version == 3) {
23541                 fg.removeClass([_this.invalidClass, _this.validClass]);
23542                 fg.addClass(_this.invalidClass);
23543             } else {
23544                 fg.removeClass(['is-invalid', 'is-valid']);
23545                 fg.addClass('is-invalid');
23546             }
23547         }
23548         
23549     },
23550     
23551     clearInvalid : function()
23552     {
23553         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23554         
23555         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23556         
23557         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23558         
23559         if (label && label.iconEl) {
23560             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23561             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23562         }
23563     },
23564     
23565     disable : function()
23566     {
23567         if(this.inputType != 'radio'){
23568             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23569             return;
23570         }
23571         
23572         var _this = this;
23573         
23574         if(this.rendered){
23575             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23576                 _this.getActionEl().addClass(this.disabledClass);
23577                 e.dom.disabled = true;
23578             });
23579         }
23580         
23581         this.disabled = true;
23582         this.fireEvent("disable", this);
23583         return this;
23584     },
23585
23586     enable : function()
23587     {
23588         if(this.inputType != 'radio'){
23589             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23590             return;
23591         }
23592         
23593         var _this = this;
23594         
23595         if(this.rendered){
23596             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23597                 _this.getActionEl().removeClass(this.disabledClass);
23598                 e.dom.disabled = false;
23599             });
23600         }
23601         
23602         this.disabled = false;
23603         this.fireEvent("enable", this);
23604         return this;
23605     },
23606     
23607     setBoxLabel : function(v)
23608     {
23609         this.boxLabel = v;
23610         
23611         if(this.rendered){
23612             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23613         }
23614     }
23615
23616 });
23617
23618 Roo.apply(Roo.bootstrap.CheckBox, {
23619     
23620     groups: {},
23621     
23622      /**
23623     * register a CheckBox Group
23624     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23625     */
23626     register : function(checkbox)
23627     {
23628         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23629             this.groups[checkbox.groupId] = {};
23630         }
23631         
23632         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23633             return;
23634         }
23635         
23636         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23637         
23638     },
23639     /**
23640     * fetch a CheckBox Group based on the group ID
23641     * @param {string} the group ID
23642     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23643     */
23644     get: function(groupId) {
23645         if (typeof(this.groups[groupId]) == 'undefined') {
23646             return false;
23647         }
23648         
23649         return this.groups[groupId] ;
23650     }
23651     
23652     
23653 });
23654 /*
23655  * - LGPL
23656  *
23657  * RadioItem
23658  * 
23659  */
23660
23661 /**
23662  * @class Roo.bootstrap.Radio
23663  * @extends Roo.bootstrap.Component
23664  * Bootstrap Radio class
23665  * @cfg {String} boxLabel - the label associated
23666  * @cfg {String} value - the value of radio
23667  * 
23668  * @constructor
23669  * Create a new Radio
23670  * @param {Object} config The config object
23671  */
23672 Roo.bootstrap.Radio = function(config){
23673     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23674     
23675 };
23676
23677 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23678     
23679     boxLabel : '',
23680     
23681     value : '',
23682     
23683     getAutoCreate : function()
23684     {
23685         var cfg = {
23686             tag : 'div',
23687             cls : 'form-group radio',
23688             cn : [
23689                 {
23690                     tag : 'label',
23691                     cls : 'box-label',
23692                     html : this.boxLabel
23693                 }
23694             ]
23695         };
23696         
23697         return cfg;
23698     },
23699     
23700     initEvents : function() 
23701     {
23702         this.parent().register(this);
23703         
23704         this.el.on('click', this.onClick, this);
23705         
23706     },
23707     
23708     onClick : function(e)
23709     {
23710         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23711             this.setChecked(true);
23712         }
23713     },
23714     
23715     setChecked : function(state, suppressEvent)
23716     {
23717         this.parent().setValue(this.value, suppressEvent);
23718         
23719     },
23720     
23721     setBoxLabel : function(v)
23722     {
23723         this.boxLabel = v;
23724         
23725         if(this.rendered){
23726             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23727         }
23728     }
23729     
23730 });
23731  
23732
23733  /*
23734  * - LGPL
23735  *
23736  * Input
23737  * 
23738  */
23739
23740 /**
23741  * @class Roo.bootstrap.SecurePass
23742  * @extends Roo.bootstrap.Input
23743  * Bootstrap SecurePass class
23744  *
23745  * 
23746  * @constructor
23747  * Create a new SecurePass
23748  * @param {Object} config The config object
23749  */
23750  
23751 Roo.bootstrap.SecurePass = function (config) {
23752     // these go here, so the translation tool can replace them..
23753     this.errors = {
23754         PwdEmpty: "Please type a password, and then retype it to confirm.",
23755         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23756         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23757         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23758         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23759         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23760         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23761         TooWeak: "Your password is Too Weak."
23762     },
23763     this.meterLabel = "Password strength:";
23764     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23765     this.meterClass = [
23766         "roo-password-meter-tooweak", 
23767         "roo-password-meter-weak", 
23768         "roo-password-meter-medium", 
23769         "roo-password-meter-strong", 
23770         "roo-password-meter-grey"
23771     ];
23772     
23773     this.errors = {};
23774     
23775     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23776 }
23777
23778 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23779     /**
23780      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23781      * {
23782      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23783      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23784      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23785      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23786      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23787      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23788      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23789      * })
23790      */
23791     // private
23792     
23793     meterWidth: 300,
23794     errorMsg :'',    
23795     errors: false,
23796     imageRoot: '/',
23797     /**
23798      * @cfg {String/Object} Label for the strength meter (defaults to
23799      * 'Password strength:')
23800      */
23801     // private
23802     meterLabel: '',
23803     /**
23804      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23805      * ['Weak', 'Medium', 'Strong'])
23806      */
23807     // private    
23808     pwdStrengths: false,    
23809     // private
23810     strength: 0,
23811     // private
23812     _lastPwd: null,
23813     // private
23814     kCapitalLetter: 0,
23815     kSmallLetter: 1,
23816     kDigit: 2,
23817     kPunctuation: 3,
23818     
23819     insecure: false,
23820     // private
23821     initEvents: function ()
23822     {
23823         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23824
23825         if (this.el.is('input[type=password]') && Roo.isSafari) {
23826             this.el.on('keydown', this.SafariOnKeyDown, this);
23827         }
23828
23829         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23830     },
23831     // private
23832     onRender: function (ct, position)
23833     {
23834         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23835         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23836         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23837
23838         this.trigger.createChild({
23839                    cn: [
23840                     {
23841                     //id: 'PwdMeter',
23842                     tag: 'div',
23843                     cls: 'roo-password-meter-grey col-xs-12',
23844                     style: {
23845                         //width: 0,
23846                         //width: this.meterWidth + 'px'                                                
23847                         }
23848                     },
23849                     {                            
23850                          cls: 'roo-password-meter-text'                          
23851                     }
23852                 ]            
23853         });
23854
23855          
23856         if (this.hideTrigger) {
23857             this.trigger.setDisplayed(false);
23858         }
23859         this.setSize(this.width || '', this.height || '');
23860     },
23861     // private
23862     onDestroy: function ()
23863     {
23864         if (this.trigger) {
23865             this.trigger.removeAllListeners();
23866             this.trigger.remove();
23867         }
23868         if (this.wrap) {
23869             this.wrap.remove();
23870         }
23871         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23872     },
23873     // private
23874     checkStrength: function ()
23875     {
23876         var pwd = this.inputEl().getValue();
23877         if (pwd == this._lastPwd) {
23878             return;
23879         }
23880
23881         var strength;
23882         if (this.ClientSideStrongPassword(pwd)) {
23883             strength = 3;
23884         } else if (this.ClientSideMediumPassword(pwd)) {
23885             strength = 2;
23886         } else if (this.ClientSideWeakPassword(pwd)) {
23887             strength = 1;
23888         } else {
23889             strength = 0;
23890         }
23891         
23892         Roo.log('strength1: ' + strength);
23893         
23894         //var pm = this.trigger.child('div/div/div').dom;
23895         var pm = this.trigger.child('div/div');
23896         pm.removeClass(this.meterClass);
23897         pm.addClass(this.meterClass[strength]);
23898                 
23899         
23900         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23901                 
23902         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23903         
23904         this._lastPwd = pwd;
23905     },
23906     reset: function ()
23907     {
23908         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23909         
23910         this._lastPwd = '';
23911         
23912         var pm = this.trigger.child('div/div');
23913         pm.removeClass(this.meterClass);
23914         pm.addClass('roo-password-meter-grey');        
23915         
23916         
23917         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23918         
23919         pt.innerHTML = '';
23920         this.inputEl().dom.type='password';
23921     },
23922     // private
23923     validateValue: function (value)
23924     {
23925         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23926             return false;
23927         }
23928         if (value.length == 0) {
23929             if (this.allowBlank) {
23930                 this.clearInvalid();
23931                 return true;
23932             }
23933
23934             this.markInvalid(this.errors.PwdEmpty);
23935             this.errorMsg = this.errors.PwdEmpty;
23936             return false;
23937         }
23938         
23939         if(this.insecure){
23940             return true;
23941         }
23942         
23943         if (!value.match(/[\x21-\x7e]+/)) {
23944             this.markInvalid(this.errors.PwdBadChar);
23945             this.errorMsg = this.errors.PwdBadChar;
23946             return false;
23947         }
23948         if (value.length < 6) {
23949             this.markInvalid(this.errors.PwdShort);
23950             this.errorMsg = this.errors.PwdShort;
23951             return false;
23952         }
23953         if (value.length > 16) {
23954             this.markInvalid(this.errors.PwdLong);
23955             this.errorMsg = this.errors.PwdLong;
23956             return false;
23957         }
23958         var strength;
23959         if (this.ClientSideStrongPassword(value)) {
23960             strength = 3;
23961         } else if (this.ClientSideMediumPassword(value)) {
23962             strength = 2;
23963         } else if (this.ClientSideWeakPassword(value)) {
23964             strength = 1;
23965         } else {
23966             strength = 0;
23967         }
23968
23969         
23970         if (strength < 2) {
23971             //this.markInvalid(this.errors.TooWeak);
23972             this.errorMsg = this.errors.TooWeak;
23973             //return false;
23974         }
23975         
23976         
23977         console.log('strength2: ' + strength);
23978         
23979         //var pm = this.trigger.child('div/div/div').dom;
23980         
23981         var pm = this.trigger.child('div/div');
23982         pm.removeClass(this.meterClass);
23983         pm.addClass(this.meterClass[strength]);
23984                 
23985         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23986                 
23987         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23988         
23989         this.errorMsg = ''; 
23990         return true;
23991     },
23992     // private
23993     CharacterSetChecks: function (type)
23994     {
23995         this.type = type;
23996         this.fResult = false;
23997     },
23998     // private
23999     isctype: function (character, type)
24000     {
24001         switch (type) {  
24002             case this.kCapitalLetter:
24003                 if (character >= 'A' && character <= 'Z') {
24004                     return true;
24005                 }
24006                 break;
24007             
24008             case this.kSmallLetter:
24009                 if (character >= 'a' && character <= 'z') {
24010                     return true;
24011                 }
24012                 break;
24013             
24014             case this.kDigit:
24015                 if (character >= '0' && character <= '9') {
24016                     return true;
24017                 }
24018                 break;
24019             
24020             case this.kPunctuation:
24021                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24022                     return true;
24023                 }
24024                 break;
24025             
24026             default:
24027                 return false;
24028         }
24029
24030     },
24031     // private
24032     IsLongEnough: function (pwd, size)
24033     {
24034         return !(pwd == null || isNaN(size) || pwd.length < size);
24035     },
24036     // private
24037     SpansEnoughCharacterSets: function (word, nb)
24038     {
24039         if (!this.IsLongEnough(word, nb))
24040         {
24041             return false;
24042         }
24043
24044         var characterSetChecks = new Array(
24045             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24046             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24047         );
24048         
24049         for (var index = 0; index < word.length; ++index) {
24050             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24051                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24052                     characterSetChecks[nCharSet].fResult = true;
24053                     break;
24054                 }
24055             }
24056         }
24057
24058         var nCharSets = 0;
24059         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24060             if (characterSetChecks[nCharSet].fResult) {
24061                 ++nCharSets;
24062             }
24063         }
24064
24065         if (nCharSets < nb) {
24066             return false;
24067         }
24068         return true;
24069     },
24070     // private
24071     ClientSideStrongPassword: function (pwd)
24072     {
24073         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24074     },
24075     // private
24076     ClientSideMediumPassword: function (pwd)
24077     {
24078         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24079     },
24080     // private
24081     ClientSideWeakPassword: function (pwd)
24082     {
24083         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24084     }
24085           
24086 })//<script type="text/javascript">
24087
24088 /*
24089  * Based  Ext JS Library 1.1.1
24090  * Copyright(c) 2006-2007, Ext JS, LLC.
24091  * LGPL
24092  *
24093  */
24094  
24095 /**
24096  * @class Roo.HtmlEditorCore
24097  * @extends Roo.Component
24098  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24099  *
24100  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24101  */
24102
24103 Roo.HtmlEditorCore = function(config){
24104     
24105     
24106     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24107     
24108     
24109     this.addEvents({
24110         /**
24111          * @event initialize
24112          * Fires when the editor is fully initialized (including the iframe)
24113          * @param {Roo.HtmlEditorCore} this
24114          */
24115         initialize: true,
24116         /**
24117          * @event activate
24118          * Fires when the editor is first receives the focus. Any insertion must wait
24119          * until after this event.
24120          * @param {Roo.HtmlEditorCore} this
24121          */
24122         activate: true,
24123          /**
24124          * @event beforesync
24125          * Fires before the textarea is updated with content from the editor iframe. Return false
24126          * to cancel the sync.
24127          * @param {Roo.HtmlEditorCore} this
24128          * @param {String} html
24129          */
24130         beforesync: true,
24131          /**
24132          * @event beforepush
24133          * Fires before the iframe editor is updated with content from the textarea. Return false
24134          * to cancel the push.
24135          * @param {Roo.HtmlEditorCore} this
24136          * @param {String} html
24137          */
24138         beforepush: true,
24139          /**
24140          * @event sync
24141          * Fires when the textarea is updated with content from the editor iframe.
24142          * @param {Roo.HtmlEditorCore} this
24143          * @param {String} html
24144          */
24145         sync: true,
24146          /**
24147          * @event push
24148          * Fires when the iframe editor is updated with content from the textarea.
24149          * @param {Roo.HtmlEditorCore} this
24150          * @param {String} html
24151          */
24152         push: true,
24153         
24154         /**
24155          * @event editorevent
24156          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24157          * @param {Roo.HtmlEditorCore} this
24158          */
24159         editorevent: true
24160         
24161     });
24162     
24163     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24164     
24165     // defaults : white / black...
24166     this.applyBlacklists();
24167     
24168     
24169     
24170 };
24171
24172
24173 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24174
24175
24176      /**
24177      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24178      */
24179     
24180     owner : false,
24181     
24182      /**
24183      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24184      *                        Roo.resizable.
24185      */
24186     resizable : false,
24187      /**
24188      * @cfg {Number} height (in pixels)
24189      */   
24190     height: 300,
24191    /**
24192      * @cfg {Number} width (in pixels)
24193      */   
24194     width: 500,
24195     
24196     /**
24197      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24198      * 
24199      */
24200     stylesheets: false,
24201     
24202     // id of frame..
24203     frameId: false,
24204     
24205     // private properties
24206     validationEvent : false,
24207     deferHeight: true,
24208     initialized : false,
24209     activated : false,
24210     sourceEditMode : false,
24211     onFocus : Roo.emptyFn,
24212     iframePad:3,
24213     hideMode:'offsets',
24214     
24215     clearUp: true,
24216     
24217     // blacklist + whitelisted elements..
24218     black: false,
24219     white: false,
24220      
24221     bodyCls : '',
24222
24223     /**
24224      * Protected method that will not generally be called directly. It
24225      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24226      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24227      */
24228     getDocMarkup : function(){
24229         // body styles..
24230         var st = '';
24231         
24232         // inherit styels from page...?? 
24233         if (this.stylesheets === false) {
24234             
24235             Roo.get(document.head).select('style').each(function(node) {
24236                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24237             });
24238             
24239             Roo.get(document.head).select('link').each(function(node) { 
24240                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24241             });
24242             
24243         } else if (!this.stylesheets.length) {
24244                 // simple..
24245                 st = '<style type="text/css">' +
24246                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24247                    '</style>';
24248         } else {
24249             for (var i in this.stylesheets) { 
24250                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24251             }
24252             
24253         }
24254         
24255         st +=  '<style type="text/css">' +
24256             'IMG { cursor: pointer } ' +
24257         '</style>';
24258
24259         var cls = 'roo-htmleditor-body';
24260         
24261         if(this.bodyCls.length){
24262             cls += ' ' + this.bodyCls;
24263         }
24264         
24265         return '<html><head>' + st  +
24266             //<style type="text/css">' +
24267             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24268             //'</style>' +
24269             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24270     },
24271
24272     // private
24273     onRender : function(ct, position)
24274     {
24275         var _t = this;
24276         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24277         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24278         
24279         
24280         this.el.dom.style.border = '0 none';
24281         this.el.dom.setAttribute('tabIndex', -1);
24282         this.el.addClass('x-hidden hide');
24283         
24284         
24285         
24286         if(Roo.isIE){ // fix IE 1px bogus margin
24287             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24288         }
24289        
24290         
24291         this.frameId = Roo.id();
24292         
24293          
24294         
24295         var iframe = this.owner.wrap.createChild({
24296             tag: 'iframe',
24297             cls: 'form-control', // bootstrap..
24298             id: this.frameId,
24299             name: this.frameId,
24300             frameBorder : 'no',
24301             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24302         }, this.el
24303         );
24304         
24305         
24306         this.iframe = iframe.dom;
24307
24308          this.assignDocWin();
24309         
24310         this.doc.designMode = 'on';
24311        
24312         this.doc.open();
24313         this.doc.write(this.getDocMarkup());
24314         this.doc.close();
24315
24316         
24317         var task = { // must defer to wait for browser to be ready
24318             run : function(){
24319                 //console.log("run task?" + this.doc.readyState);
24320                 this.assignDocWin();
24321                 if(this.doc.body || this.doc.readyState == 'complete'){
24322                     try {
24323                         this.doc.designMode="on";
24324                     } catch (e) {
24325                         return;
24326                     }
24327                     Roo.TaskMgr.stop(task);
24328                     this.initEditor.defer(10, this);
24329                 }
24330             },
24331             interval : 10,
24332             duration: 10000,
24333             scope: this
24334         };
24335         Roo.TaskMgr.start(task);
24336
24337     },
24338
24339     // private
24340     onResize : function(w, h)
24341     {
24342          Roo.log('resize: ' +w + ',' + h );
24343         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24344         if(!this.iframe){
24345             return;
24346         }
24347         if(typeof w == 'number'){
24348             
24349             this.iframe.style.width = w + 'px';
24350         }
24351         if(typeof h == 'number'){
24352             
24353             this.iframe.style.height = h + 'px';
24354             if(this.doc){
24355                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24356             }
24357         }
24358         
24359     },
24360
24361     /**
24362      * Toggles the editor between standard and source edit mode.
24363      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24364      */
24365     toggleSourceEdit : function(sourceEditMode){
24366         
24367         this.sourceEditMode = sourceEditMode === true;
24368         
24369         if(this.sourceEditMode){
24370  
24371             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24372             
24373         }else{
24374             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24375             //this.iframe.className = '';
24376             this.deferFocus();
24377         }
24378         //this.setSize(this.owner.wrap.getSize());
24379         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24380     },
24381
24382     
24383   
24384
24385     /**
24386      * Protected method that will not generally be called directly. If you need/want
24387      * custom HTML cleanup, this is the method you should override.
24388      * @param {String} html The HTML to be cleaned
24389      * return {String} The cleaned HTML
24390      */
24391     cleanHtml : function(html){
24392         html = String(html);
24393         if(html.length > 5){
24394             if(Roo.isSafari){ // strip safari nonsense
24395                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24396             }
24397         }
24398         if(html == '&nbsp;'){
24399             html = '';
24400         }
24401         return html;
24402     },
24403
24404     /**
24405      * HTML Editor -> Textarea
24406      * Protected method that will not generally be called directly. Syncs the contents
24407      * of the editor iframe with the textarea.
24408      */
24409     syncValue : function(){
24410         if(this.initialized){
24411             var bd = (this.doc.body || this.doc.documentElement);
24412             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24413             var html = bd.innerHTML;
24414             if(Roo.isSafari){
24415                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24416                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24417                 if(m && m[1]){
24418                     html = '<div style="'+m[0]+'">' + html + '</div>';
24419                 }
24420             }
24421             html = this.cleanHtml(html);
24422             // fix up the special chars.. normaly like back quotes in word...
24423             // however we do not want to do this with chinese..
24424             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24425                 
24426                 var cc = match.charCodeAt();
24427
24428                 // Get the character value, handling surrogate pairs
24429                 if (match.length == 2) {
24430                     // It's a surrogate pair, calculate the Unicode code point
24431                     var high = match.charCodeAt(0) - 0xD800;
24432                     var low  = match.charCodeAt(1) - 0xDC00;
24433                     cc = (high * 0x400) + low + 0x10000;
24434                 }  else if (
24435                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24436                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24437                     (cc >= 0xf900 && cc < 0xfb00 )
24438                 ) {
24439                         return match;
24440                 }  
24441          
24442                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24443                 return "&#" + cc + ";";
24444                 
24445                 
24446             });
24447             
24448             
24449              
24450             if(this.owner.fireEvent('beforesync', this, html) !== false){
24451                 this.el.dom.value = html;
24452                 this.owner.fireEvent('sync', this, html);
24453             }
24454         }
24455     },
24456
24457     /**
24458      * Protected method that will not generally be called directly. Pushes the value of the textarea
24459      * into the iframe editor.
24460      */
24461     pushValue : function(){
24462         if(this.initialized){
24463             var v = this.el.dom.value.trim();
24464             
24465 //            if(v.length < 1){
24466 //                v = '&#160;';
24467 //            }
24468             
24469             if(this.owner.fireEvent('beforepush', this, v) !== false){
24470                 var d = (this.doc.body || this.doc.documentElement);
24471                 d.innerHTML = v;
24472                 this.cleanUpPaste();
24473                 this.el.dom.value = d.innerHTML;
24474                 this.owner.fireEvent('push', this, v);
24475             }
24476         }
24477     },
24478
24479     // private
24480     deferFocus : function(){
24481         this.focus.defer(10, this);
24482     },
24483
24484     // doc'ed in Field
24485     focus : function(){
24486         if(this.win && !this.sourceEditMode){
24487             this.win.focus();
24488         }else{
24489             this.el.focus();
24490         }
24491     },
24492     
24493     assignDocWin: function()
24494     {
24495         var iframe = this.iframe;
24496         
24497          if(Roo.isIE){
24498             this.doc = iframe.contentWindow.document;
24499             this.win = iframe.contentWindow;
24500         } else {
24501 //            if (!Roo.get(this.frameId)) {
24502 //                return;
24503 //            }
24504 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24505 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24506             
24507             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24508                 return;
24509             }
24510             
24511             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24512             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24513         }
24514     },
24515     
24516     // private
24517     initEditor : function(){
24518         //console.log("INIT EDITOR");
24519         this.assignDocWin();
24520         
24521         
24522         
24523         this.doc.designMode="on";
24524         this.doc.open();
24525         this.doc.write(this.getDocMarkup());
24526         this.doc.close();
24527         
24528         var dbody = (this.doc.body || this.doc.documentElement);
24529         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24530         // this copies styles from the containing element into thsi one..
24531         // not sure why we need all of this..
24532         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24533         
24534         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24535         //ss['background-attachment'] = 'fixed'; // w3c
24536         dbody.bgProperties = 'fixed'; // ie
24537         //Roo.DomHelper.applyStyles(dbody, ss);
24538         Roo.EventManager.on(this.doc, {
24539             //'mousedown': this.onEditorEvent,
24540             'mouseup': this.onEditorEvent,
24541             'dblclick': this.onEditorEvent,
24542             'click': this.onEditorEvent,
24543             'keyup': this.onEditorEvent,
24544             buffer:100,
24545             scope: this
24546         });
24547         if(Roo.isGecko){
24548             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24549         }
24550         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24551             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24552         }
24553         this.initialized = true;
24554
24555         this.owner.fireEvent('initialize', this);
24556         this.pushValue();
24557     },
24558
24559     // private
24560     onDestroy : function(){
24561         
24562         
24563         
24564         if(this.rendered){
24565             
24566             //for (var i =0; i < this.toolbars.length;i++) {
24567             //    // fixme - ask toolbars for heights?
24568             //    this.toolbars[i].onDestroy();
24569            // }
24570             
24571             //this.wrap.dom.innerHTML = '';
24572             //this.wrap.remove();
24573         }
24574     },
24575
24576     // private
24577     onFirstFocus : function(){
24578         
24579         this.assignDocWin();
24580         
24581         
24582         this.activated = true;
24583          
24584     
24585         if(Roo.isGecko){ // prevent silly gecko errors
24586             this.win.focus();
24587             var s = this.win.getSelection();
24588             if(!s.focusNode || s.focusNode.nodeType != 3){
24589                 var r = s.getRangeAt(0);
24590                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24591                 r.collapse(true);
24592                 this.deferFocus();
24593             }
24594             try{
24595                 this.execCmd('useCSS', true);
24596                 this.execCmd('styleWithCSS', false);
24597             }catch(e){}
24598         }
24599         this.owner.fireEvent('activate', this);
24600     },
24601
24602     // private
24603     adjustFont: function(btn){
24604         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24605         //if(Roo.isSafari){ // safari
24606         //    adjust *= 2;
24607        // }
24608         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24609         if(Roo.isSafari){ // safari
24610             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24611             v =  (v < 10) ? 10 : v;
24612             v =  (v > 48) ? 48 : v;
24613             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24614             
24615         }
24616         
24617         
24618         v = Math.max(1, v+adjust);
24619         
24620         this.execCmd('FontSize', v  );
24621     },
24622
24623     onEditorEvent : function(e)
24624     {
24625         this.owner.fireEvent('editorevent', this, e);
24626       //  this.updateToolbar();
24627         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24628     },
24629
24630     insertTag : function(tg)
24631     {
24632         // could be a bit smarter... -> wrap the current selected tRoo..
24633         if (tg.toLowerCase() == 'span' ||
24634             tg.toLowerCase() == 'code' ||
24635             tg.toLowerCase() == 'sup' ||
24636             tg.toLowerCase() == 'sub' 
24637             ) {
24638             
24639             range = this.createRange(this.getSelection());
24640             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24641             wrappingNode.appendChild(range.extractContents());
24642             range.insertNode(wrappingNode);
24643
24644             return;
24645             
24646             
24647             
24648         }
24649         this.execCmd("formatblock",   tg);
24650         
24651     },
24652     
24653     insertText : function(txt)
24654     {
24655         
24656         
24657         var range = this.createRange();
24658         range.deleteContents();
24659                //alert(Sender.getAttribute('label'));
24660                
24661         range.insertNode(this.doc.createTextNode(txt));
24662     } ,
24663     
24664      
24665
24666     /**
24667      * Executes a Midas editor command on the editor document and performs necessary focus and
24668      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24669      * @param {String} cmd The Midas command
24670      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24671      */
24672     relayCmd : function(cmd, value){
24673         this.win.focus();
24674         this.execCmd(cmd, value);
24675         this.owner.fireEvent('editorevent', this);
24676         //this.updateToolbar();
24677         this.owner.deferFocus();
24678     },
24679
24680     /**
24681      * Executes a Midas editor command directly on the editor document.
24682      * For visual commands, you should use {@link #relayCmd} instead.
24683      * <b>This should only be called after the editor is initialized.</b>
24684      * @param {String} cmd The Midas command
24685      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24686      */
24687     execCmd : function(cmd, value){
24688         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24689         this.syncValue();
24690     },
24691  
24692  
24693    
24694     /**
24695      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24696      * to insert tRoo.
24697      * @param {String} text | dom node.. 
24698      */
24699     insertAtCursor : function(text)
24700     {
24701         
24702         if(!this.activated){
24703             return;
24704         }
24705         /*
24706         if(Roo.isIE){
24707             this.win.focus();
24708             var r = this.doc.selection.createRange();
24709             if(r){
24710                 r.collapse(true);
24711                 r.pasteHTML(text);
24712                 this.syncValue();
24713                 this.deferFocus();
24714             
24715             }
24716             return;
24717         }
24718         */
24719         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24720             this.win.focus();
24721             
24722             
24723             // from jquery ui (MIT licenced)
24724             var range, node;
24725             var win = this.win;
24726             
24727             if (win.getSelection && win.getSelection().getRangeAt) {
24728                 range = win.getSelection().getRangeAt(0);
24729                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24730                 range.insertNode(node);
24731             } else if (win.document.selection && win.document.selection.createRange) {
24732                 // no firefox support
24733                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24734                 win.document.selection.createRange().pasteHTML(txt);
24735             } else {
24736                 // no firefox support
24737                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24738                 this.execCmd('InsertHTML', txt);
24739             } 
24740             
24741             this.syncValue();
24742             
24743             this.deferFocus();
24744         }
24745     },
24746  // private
24747     mozKeyPress : function(e){
24748         if(e.ctrlKey){
24749             var c = e.getCharCode(), cmd;
24750           
24751             if(c > 0){
24752                 c = String.fromCharCode(c).toLowerCase();
24753                 switch(c){
24754                     case 'b':
24755                         cmd = 'bold';
24756                         break;
24757                     case 'i':
24758                         cmd = 'italic';
24759                         break;
24760                     
24761                     case 'u':
24762                         cmd = 'underline';
24763                         break;
24764                     
24765                     case 'v':
24766                         this.cleanUpPaste.defer(100, this);
24767                         return;
24768                         
24769                 }
24770                 if(cmd){
24771                     this.win.focus();
24772                     this.execCmd(cmd);
24773                     this.deferFocus();
24774                     e.preventDefault();
24775                 }
24776                 
24777             }
24778         }
24779     },
24780
24781     // private
24782     fixKeys : function(){ // load time branching for fastest keydown performance
24783         if(Roo.isIE){
24784             return function(e){
24785                 var k = e.getKey(), r;
24786                 if(k == e.TAB){
24787                     e.stopEvent();
24788                     r = this.doc.selection.createRange();
24789                     if(r){
24790                         r.collapse(true);
24791                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24792                         this.deferFocus();
24793                     }
24794                     return;
24795                 }
24796                 
24797                 if(k == e.ENTER){
24798                     r = this.doc.selection.createRange();
24799                     if(r){
24800                         var target = r.parentElement();
24801                         if(!target || target.tagName.toLowerCase() != 'li'){
24802                             e.stopEvent();
24803                             r.pasteHTML('<br />');
24804                             r.collapse(false);
24805                             r.select();
24806                         }
24807                     }
24808                 }
24809                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24810                     this.cleanUpPaste.defer(100, this);
24811                     return;
24812                 }
24813                 
24814                 
24815             };
24816         }else if(Roo.isOpera){
24817             return function(e){
24818                 var k = e.getKey();
24819                 if(k == e.TAB){
24820                     e.stopEvent();
24821                     this.win.focus();
24822                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24823                     this.deferFocus();
24824                 }
24825                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24826                     this.cleanUpPaste.defer(100, this);
24827                     return;
24828                 }
24829                 
24830             };
24831         }else if(Roo.isSafari){
24832             return function(e){
24833                 var k = e.getKey();
24834                 
24835                 if(k == e.TAB){
24836                     e.stopEvent();
24837                     this.execCmd('InsertText','\t');
24838                     this.deferFocus();
24839                     return;
24840                 }
24841                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24842                     this.cleanUpPaste.defer(100, this);
24843                     return;
24844                 }
24845                 
24846              };
24847         }
24848     }(),
24849     
24850     getAllAncestors: function()
24851     {
24852         var p = this.getSelectedNode();
24853         var a = [];
24854         if (!p) {
24855             a.push(p); // push blank onto stack..
24856             p = this.getParentElement();
24857         }
24858         
24859         
24860         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24861             a.push(p);
24862             p = p.parentNode;
24863         }
24864         a.push(this.doc.body);
24865         return a;
24866     },
24867     lastSel : false,
24868     lastSelNode : false,
24869     
24870     
24871     getSelection : function() 
24872     {
24873         this.assignDocWin();
24874         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24875     },
24876     
24877     getSelectedNode: function() 
24878     {
24879         // this may only work on Gecko!!!
24880         
24881         // should we cache this!!!!
24882         
24883         
24884         
24885          
24886         var range = this.createRange(this.getSelection()).cloneRange();
24887         
24888         if (Roo.isIE) {
24889             var parent = range.parentElement();
24890             while (true) {
24891                 var testRange = range.duplicate();
24892                 testRange.moveToElementText(parent);
24893                 if (testRange.inRange(range)) {
24894                     break;
24895                 }
24896                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24897                     break;
24898                 }
24899                 parent = parent.parentElement;
24900             }
24901             return parent;
24902         }
24903         
24904         // is ancestor a text element.
24905         var ac =  range.commonAncestorContainer;
24906         if (ac.nodeType == 3) {
24907             ac = ac.parentNode;
24908         }
24909         
24910         var ar = ac.childNodes;
24911          
24912         var nodes = [];
24913         var other_nodes = [];
24914         var has_other_nodes = false;
24915         for (var i=0;i<ar.length;i++) {
24916             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24917                 continue;
24918             }
24919             // fullly contained node.
24920             
24921             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24922                 nodes.push(ar[i]);
24923                 continue;
24924             }
24925             
24926             // probably selected..
24927             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24928                 other_nodes.push(ar[i]);
24929                 continue;
24930             }
24931             // outer..
24932             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24933                 continue;
24934             }
24935             
24936             
24937             has_other_nodes = true;
24938         }
24939         if (!nodes.length && other_nodes.length) {
24940             nodes= other_nodes;
24941         }
24942         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24943             return false;
24944         }
24945         
24946         return nodes[0];
24947     },
24948     createRange: function(sel)
24949     {
24950         // this has strange effects when using with 
24951         // top toolbar - not sure if it's a great idea.
24952         //this.editor.contentWindow.focus();
24953         if (typeof sel != "undefined") {
24954             try {
24955                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24956             } catch(e) {
24957                 return this.doc.createRange();
24958             }
24959         } else {
24960             return this.doc.createRange();
24961         }
24962     },
24963     getParentElement: function()
24964     {
24965         
24966         this.assignDocWin();
24967         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24968         
24969         var range = this.createRange(sel);
24970          
24971         try {
24972             var p = range.commonAncestorContainer;
24973             while (p.nodeType == 3) { // text node
24974                 p = p.parentNode;
24975             }
24976             return p;
24977         } catch (e) {
24978             return null;
24979         }
24980     
24981     },
24982     /***
24983      *
24984      * Range intersection.. the hard stuff...
24985      *  '-1' = before
24986      *  '0' = hits..
24987      *  '1' = after.
24988      *         [ -- selected range --- ]
24989      *   [fail]                        [fail]
24990      *
24991      *    basically..
24992      *      if end is before start or  hits it. fail.
24993      *      if start is after end or hits it fail.
24994      *
24995      *   if either hits (but other is outside. - then it's not 
24996      *   
24997      *    
24998      **/
24999     
25000     
25001     // @see http://www.thismuchiknow.co.uk/?p=64.
25002     rangeIntersectsNode : function(range, node)
25003     {
25004         var nodeRange = node.ownerDocument.createRange();
25005         try {
25006             nodeRange.selectNode(node);
25007         } catch (e) {
25008             nodeRange.selectNodeContents(node);
25009         }
25010     
25011         var rangeStartRange = range.cloneRange();
25012         rangeStartRange.collapse(true);
25013     
25014         var rangeEndRange = range.cloneRange();
25015         rangeEndRange.collapse(false);
25016     
25017         var nodeStartRange = nodeRange.cloneRange();
25018         nodeStartRange.collapse(true);
25019     
25020         var nodeEndRange = nodeRange.cloneRange();
25021         nodeEndRange.collapse(false);
25022     
25023         return rangeStartRange.compareBoundaryPoints(
25024                  Range.START_TO_START, nodeEndRange) == -1 &&
25025                rangeEndRange.compareBoundaryPoints(
25026                  Range.START_TO_START, nodeStartRange) == 1;
25027         
25028          
25029     },
25030     rangeCompareNode : function(range, node)
25031     {
25032         var nodeRange = node.ownerDocument.createRange();
25033         try {
25034             nodeRange.selectNode(node);
25035         } catch (e) {
25036             nodeRange.selectNodeContents(node);
25037         }
25038         
25039         
25040         range.collapse(true);
25041     
25042         nodeRange.collapse(true);
25043      
25044         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25045         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25046          
25047         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25048         
25049         var nodeIsBefore   =  ss == 1;
25050         var nodeIsAfter    = ee == -1;
25051         
25052         if (nodeIsBefore && nodeIsAfter) {
25053             return 0; // outer
25054         }
25055         if (!nodeIsBefore && nodeIsAfter) {
25056             return 1; //right trailed.
25057         }
25058         
25059         if (nodeIsBefore && !nodeIsAfter) {
25060             return 2;  // left trailed.
25061         }
25062         // fully contined.
25063         return 3;
25064     },
25065
25066     // private? - in a new class?
25067     cleanUpPaste :  function()
25068     {
25069         // cleans up the whole document..
25070         Roo.log('cleanuppaste');
25071         
25072         this.cleanUpChildren(this.doc.body);
25073         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25074         if (clean != this.doc.body.innerHTML) {
25075             this.doc.body.innerHTML = clean;
25076         }
25077         
25078     },
25079     
25080     cleanWordChars : function(input) {// change the chars to hex code
25081         var he = Roo.HtmlEditorCore;
25082         
25083         var output = input;
25084         Roo.each(he.swapCodes, function(sw) { 
25085             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25086             
25087             output = output.replace(swapper, sw[1]);
25088         });
25089         
25090         return output;
25091     },
25092     
25093     
25094     cleanUpChildren : function (n)
25095     {
25096         if (!n.childNodes.length) {
25097             return;
25098         }
25099         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25100            this.cleanUpChild(n.childNodes[i]);
25101         }
25102     },
25103     
25104     
25105         
25106     
25107     cleanUpChild : function (node)
25108     {
25109         var ed = this;
25110         //console.log(node);
25111         if (node.nodeName == "#text") {
25112             // clean up silly Windows -- stuff?
25113             return; 
25114         }
25115         if (node.nodeName == "#comment") {
25116             node.parentNode.removeChild(node);
25117             // clean up silly Windows -- stuff?
25118             return; 
25119         }
25120         var lcname = node.tagName.toLowerCase();
25121         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25122         // whitelist of tags..
25123         
25124         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25125             // remove node.
25126             node.parentNode.removeChild(node);
25127             return;
25128             
25129         }
25130         
25131         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25132         
25133         // spans with no attributes - just remove them..
25134         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25135             remove_keep_children = true;
25136         }
25137         
25138         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25139         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25140         
25141         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25142         //    remove_keep_children = true;
25143         //}
25144         
25145         if (remove_keep_children) {
25146             this.cleanUpChildren(node);
25147             // inserts everything just before this node...
25148             while (node.childNodes.length) {
25149                 var cn = node.childNodes[0];
25150                 node.removeChild(cn);
25151                 node.parentNode.insertBefore(cn, node);
25152             }
25153             node.parentNode.removeChild(node);
25154             return;
25155         }
25156         
25157         if (!node.attributes || !node.attributes.length) {
25158             
25159           
25160             
25161             
25162             this.cleanUpChildren(node);
25163             return;
25164         }
25165         
25166         function cleanAttr(n,v)
25167         {
25168             
25169             if (v.match(/^\./) || v.match(/^\//)) {
25170                 return;
25171             }
25172             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25173                 return;
25174             }
25175             if (v.match(/^#/)) {
25176                 return;
25177             }
25178             if (v.match(/^\{/)) { // allow template editing.
25179                 return;
25180             }
25181 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25182             node.removeAttribute(n);
25183             
25184         }
25185         
25186         var cwhite = this.cwhite;
25187         var cblack = this.cblack;
25188             
25189         function cleanStyle(n,v)
25190         {
25191             if (v.match(/expression/)) { //XSS?? should we even bother..
25192                 node.removeAttribute(n);
25193                 return;
25194             }
25195             
25196             var parts = v.split(/;/);
25197             var clean = [];
25198             
25199             Roo.each(parts, function(p) {
25200                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25201                 if (!p.length) {
25202                     return true;
25203                 }
25204                 var l = p.split(':').shift().replace(/\s+/g,'');
25205                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25206                 
25207                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25208 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25209                     //node.removeAttribute(n);
25210                     return true;
25211                 }
25212                 //Roo.log()
25213                 // only allow 'c whitelisted system attributes'
25214                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25215 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25216                     //node.removeAttribute(n);
25217                     return true;
25218                 }
25219                 
25220                 
25221                  
25222                 
25223                 clean.push(p);
25224                 return true;
25225             });
25226             if (clean.length) { 
25227                 node.setAttribute(n, clean.join(';'));
25228             } else {
25229                 node.removeAttribute(n);
25230             }
25231             
25232         }
25233         
25234         
25235         for (var i = node.attributes.length-1; i > -1 ; i--) {
25236             var a = node.attributes[i];
25237             //console.log(a);
25238             
25239             if (a.name.toLowerCase().substr(0,2)=='on')  {
25240                 node.removeAttribute(a.name);
25241                 continue;
25242             }
25243             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25244                 node.removeAttribute(a.name);
25245                 continue;
25246             }
25247             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25248                 cleanAttr(a.name,a.value); // fixme..
25249                 continue;
25250             }
25251             if (a.name == 'style') {
25252                 cleanStyle(a.name,a.value);
25253                 continue;
25254             }
25255             /// clean up MS crap..
25256             // tecnically this should be a list of valid class'es..
25257             
25258             
25259             if (a.name == 'class') {
25260                 if (a.value.match(/^Mso/)) {
25261                     node.removeAttribute('class');
25262                 }
25263                 
25264                 if (a.value.match(/^body$/)) {
25265                     node.removeAttribute('class');
25266                 }
25267                 continue;
25268             }
25269             
25270             // style cleanup!?
25271             // class cleanup?
25272             
25273         }
25274         
25275         
25276         this.cleanUpChildren(node);
25277         
25278         
25279     },
25280     
25281     /**
25282      * Clean up MS wordisms...
25283      */
25284     cleanWord : function(node)
25285     {
25286         if (!node) {
25287             this.cleanWord(this.doc.body);
25288             return;
25289         }
25290         
25291         if(
25292                 node.nodeName == 'SPAN' &&
25293                 !node.hasAttributes() &&
25294                 node.childNodes.length == 1 &&
25295                 node.firstChild.nodeName == "#text"  
25296         ) {
25297             var textNode = node.firstChild;
25298             node.removeChild(textNode);
25299             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25300                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25301             }
25302             node.parentNode.insertBefore(textNode, node);
25303             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25304                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25305             }
25306             node.parentNode.removeChild(node);
25307         }
25308         
25309         if (node.nodeName == "#text") {
25310             // clean up silly Windows -- stuff?
25311             return; 
25312         }
25313         if (node.nodeName == "#comment") {
25314             node.parentNode.removeChild(node);
25315             // clean up silly Windows -- stuff?
25316             return; 
25317         }
25318         
25319         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25320             node.parentNode.removeChild(node);
25321             return;
25322         }
25323         //Roo.log(node.tagName);
25324         // remove - but keep children..
25325         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25326             //Roo.log('-- removed');
25327             while (node.childNodes.length) {
25328                 var cn = node.childNodes[0];
25329                 node.removeChild(cn);
25330                 node.parentNode.insertBefore(cn, node);
25331                 // move node to parent - and clean it..
25332                 this.cleanWord(cn);
25333             }
25334             node.parentNode.removeChild(node);
25335             /// no need to iterate chidlren = it's got none..
25336             //this.iterateChildren(node, this.cleanWord);
25337             return;
25338         }
25339         // clean styles
25340         if (node.className.length) {
25341             
25342             var cn = node.className.split(/\W+/);
25343             var cna = [];
25344             Roo.each(cn, function(cls) {
25345                 if (cls.match(/Mso[a-zA-Z]+/)) {
25346                     return;
25347                 }
25348                 cna.push(cls);
25349             });
25350             node.className = cna.length ? cna.join(' ') : '';
25351             if (!cna.length) {
25352                 node.removeAttribute("class");
25353             }
25354         }
25355         
25356         if (node.hasAttribute("lang")) {
25357             node.removeAttribute("lang");
25358         }
25359         
25360         if (node.hasAttribute("style")) {
25361             
25362             var styles = node.getAttribute("style").split(";");
25363             var nstyle = [];
25364             Roo.each(styles, function(s) {
25365                 if (!s.match(/:/)) {
25366                     return;
25367                 }
25368                 var kv = s.split(":");
25369                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25370                     return;
25371                 }
25372                 // what ever is left... we allow.
25373                 nstyle.push(s);
25374             });
25375             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25376             if (!nstyle.length) {
25377                 node.removeAttribute('style');
25378             }
25379         }
25380         this.iterateChildren(node, this.cleanWord);
25381         
25382         
25383         
25384     },
25385     /**
25386      * iterateChildren of a Node, calling fn each time, using this as the scole..
25387      * @param {DomNode} node node to iterate children of.
25388      * @param {Function} fn method of this class to call on each item.
25389      */
25390     iterateChildren : function(node, fn)
25391     {
25392         if (!node.childNodes.length) {
25393                 return;
25394         }
25395         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25396            fn.call(this, node.childNodes[i])
25397         }
25398     },
25399     
25400     
25401     /**
25402      * cleanTableWidths.
25403      *
25404      * Quite often pasting from word etc.. results in tables with column and widths.
25405      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25406      *
25407      */
25408     cleanTableWidths : function(node)
25409     {
25410          
25411          
25412         if (!node) {
25413             this.cleanTableWidths(this.doc.body);
25414             return;
25415         }
25416         
25417         // ignore list...
25418         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25419             return; 
25420         }
25421         Roo.log(node.tagName);
25422         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25423             this.iterateChildren(node, this.cleanTableWidths);
25424             return;
25425         }
25426         if (node.hasAttribute('width')) {
25427             node.removeAttribute('width');
25428         }
25429         
25430          
25431         if (node.hasAttribute("style")) {
25432             // pretty basic...
25433             
25434             var styles = node.getAttribute("style").split(";");
25435             var nstyle = [];
25436             Roo.each(styles, function(s) {
25437                 if (!s.match(/:/)) {
25438                     return;
25439                 }
25440                 var kv = s.split(":");
25441                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25442                     return;
25443                 }
25444                 // what ever is left... we allow.
25445                 nstyle.push(s);
25446             });
25447             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25448             if (!nstyle.length) {
25449                 node.removeAttribute('style');
25450             }
25451         }
25452         
25453         this.iterateChildren(node, this.cleanTableWidths);
25454         
25455         
25456     },
25457     
25458     
25459     
25460     
25461     domToHTML : function(currentElement, depth, nopadtext) {
25462         
25463         depth = depth || 0;
25464         nopadtext = nopadtext || false;
25465     
25466         if (!currentElement) {
25467             return this.domToHTML(this.doc.body);
25468         }
25469         
25470         //Roo.log(currentElement);
25471         var j;
25472         var allText = false;
25473         var nodeName = currentElement.nodeName;
25474         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25475         
25476         if  (nodeName == '#text') {
25477             
25478             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25479         }
25480         
25481         
25482         var ret = '';
25483         if (nodeName != 'BODY') {
25484              
25485             var i = 0;
25486             // Prints the node tagName, such as <A>, <IMG>, etc
25487             if (tagName) {
25488                 var attr = [];
25489                 for(i = 0; i < currentElement.attributes.length;i++) {
25490                     // quoting?
25491                     var aname = currentElement.attributes.item(i).name;
25492                     if (!currentElement.attributes.item(i).value.length) {
25493                         continue;
25494                     }
25495                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25496                 }
25497                 
25498                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25499             } 
25500             else {
25501                 
25502                 // eack
25503             }
25504         } else {
25505             tagName = false;
25506         }
25507         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25508             return ret;
25509         }
25510         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25511             nopadtext = true;
25512         }
25513         
25514         
25515         // Traverse the tree
25516         i = 0;
25517         var currentElementChild = currentElement.childNodes.item(i);
25518         var allText = true;
25519         var innerHTML  = '';
25520         lastnode = '';
25521         while (currentElementChild) {
25522             // Formatting code (indent the tree so it looks nice on the screen)
25523             var nopad = nopadtext;
25524             if (lastnode == 'SPAN') {
25525                 nopad  = true;
25526             }
25527             // text
25528             if  (currentElementChild.nodeName == '#text') {
25529                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25530                 toadd = nopadtext ? toadd : toadd.trim();
25531                 if (!nopad && toadd.length > 80) {
25532                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25533                 }
25534                 innerHTML  += toadd;
25535                 
25536                 i++;
25537                 currentElementChild = currentElement.childNodes.item(i);
25538                 lastNode = '';
25539                 continue;
25540             }
25541             allText = false;
25542             
25543             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25544                 
25545             // Recursively traverse the tree structure of the child node
25546             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25547             lastnode = currentElementChild.nodeName;
25548             i++;
25549             currentElementChild=currentElement.childNodes.item(i);
25550         }
25551         
25552         ret += innerHTML;
25553         
25554         if (!allText) {
25555                 // The remaining code is mostly for formatting the tree
25556             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25557         }
25558         
25559         
25560         if (tagName) {
25561             ret+= "</"+tagName+">";
25562         }
25563         return ret;
25564         
25565     },
25566         
25567     applyBlacklists : function()
25568     {
25569         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25570         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25571         
25572         this.white = [];
25573         this.black = [];
25574         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25575             if (b.indexOf(tag) > -1) {
25576                 return;
25577             }
25578             this.white.push(tag);
25579             
25580         }, this);
25581         
25582         Roo.each(w, function(tag) {
25583             if (b.indexOf(tag) > -1) {
25584                 return;
25585             }
25586             if (this.white.indexOf(tag) > -1) {
25587                 return;
25588             }
25589             this.white.push(tag);
25590             
25591         }, this);
25592         
25593         
25594         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25595             if (w.indexOf(tag) > -1) {
25596                 return;
25597             }
25598             this.black.push(tag);
25599             
25600         }, this);
25601         
25602         Roo.each(b, function(tag) {
25603             if (w.indexOf(tag) > -1) {
25604                 return;
25605             }
25606             if (this.black.indexOf(tag) > -1) {
25607                 return;
25608             }
25609             this.black.push(tag);
25610             
25611         }, this);
25612         
25613         
25614         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25615         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25616         
25617         this.cwhite = [];
25618         this.cblack = [];
25619         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25620             if (b.indexOf(tag) > -1) {
25621                 return;
25622             }
25623             this.cwhite.push(tag);
25624             
25625         }, this);
25626         
25627         Roo.each(w, function(tag) {
25628             if (b.indexOf(tag) > -1) {
25629                 return;
25630             }
25631             if (this.cwhite.indexOf(tag) > -1) {
25632                 return;
25633             }
25634             this.cwhite.push(tag);
25635             
25636         }, this);
25637         
25638         
25639         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25640             if (w.indexOf(tag) > -1) {
25641                 return;
25642             }
25643             this.cblack.push(tag);
25644             
25645         }, this);
25646         
25647         Roo.each(b, function(tag) {
25648             if (w.indexOf(tag) > -1) {
25649                 return;
25650             }
25651             if (this.cblack.indexOf(tag) > -1) {
25652                 return;
25653             }
25654             this.cblack.push(tag);
25655             
25656         }, this);
25657     },
25658     
25659     setStylesheets : function(stylesheets)
25660     {
25661         if(typeof(stylesheets) == 'string'){
25662             Roo.get(this.iframe.contentDocument.head).createChild({
25663                 tag : 'link',
25664                 rel : 'stylesheet',
25665                 type : 'text/css',
25666                 href : stylesheets
25667             });
25668             
25669             return;
25670         }
25671         var _this = this;
25672      
25673         Roo.each(stylesheets, function(s) {
25674             if(!s.length){
25675                 return;
25676             }
25677             
25678             Roo.get(_this.iframe.contentDocument.head).createChild({
25679                 tag : 'link',
25680                 rel : 'stylesheet',
25681                 type : 'text/css',
25682                 href : s
25683             });
25684         });
25685
25686         
25687     },
25688     
25689     removeStylesheets : function()
25690     {
25691         var _this = this;
25692         
25693         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25694             s.remove();
25695         });
25696     },
25697     
25698     setStyle : function(style)
25699     {
25700         Roo.get(this.iframe.contentDocument.head).createChild({
25701             tag : 'style',
25702             type : 'text/css',
25703             html : style
25704         });
25705
25706         return;
25707     }
25708     
25709     // hide stuff that is not compatible
25710     /**
25711      * @event blur
25712      * @hide
25713      */
25714     /**
25715      * @event change
25716      * @hide
25717      */
25718     /**
25719      * @event focus
25720      * @hide
25721      */
25722     /**
25723      * @event specialkey
25724      * @hide
25725      */
25726     /**
25727      * @cfg {String} fieldClass @hide
25728      */
25729     /**
25730      * @cfg {String} focusClass @hide
25731      */
25732     /**
25733      * @cfg {String} autoCreate @hide
25734      */
25735     /**
25736      * @cfg {String} inputType @hide
25737      */
25738     /**
25739      * @cfg {String} invalidClass @hide
25740      */
25741     /**
25742      * @cfg {String} invalidText @hide
25743      */
25744     /**
25745      * @cfg {String} msgFx @hide
25746      */
25747     /**
25748      * @cfg {String} validateOnBlur @hide
25749      */
25750 });
25751
25752 Roo.HtmlEditorCore.white = [
25753         'area', 'br', 'img', 'input', 'hr', 'wbr',
25754         
25755        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25756        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25757        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25758        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25759        'table',   'ul',         'xmp', 
25760        
25761        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25762       'thead',   'tr', 
25763      
25764       'dir', 'menu', 'ol', 'ul', 'dl',
25765        
25766       'embed',  'object'
25767 ];
25768
25769
25770 Roo.HtmlEditorCore.black = [
25771     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25772         'applet', // 
25773         'base',   'basefont', 'bgsound', 'blink',  'body', 
25774         'frame',  'frameset', 'head',    'html',   'ilayer', 
25775         'iframe', 'layer',  'link',     'meta',    'object',   
25776         'script', 'style' ,'title',  'xml' // clean later..
25777 ];
25778 Roo.HtmlEditorCore.clean = [
25779     'script', 'style', 'title', 'xml'
25780 ];
25781 Roo.HtmlEditorCore.remove = [
25782     'font'
25783 ];
25784 // attributes..
25785
25786 Roo.HtmlEditorCore.ablack = [
25787     'on'
25788 ];
25789     
25790 Roo.HtmlEditorCore.aclean = [ 
25791     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25792 ];
25793
25794 // protocols..
25795 Roo.HtmlEditorCore.pwhite= [
25796         'http',  'https',  'mailto'
25797 ];
25798
25799 // white listed style attributes.
25800 Roo.HtmlEditorCore.cwhite= [
25801       //  'text-align', /// default is to allow most things..
25802       
25803          
25804 //        'font-size'//??
25805 ];
25806
25807 // black listed style attributes.
25808 Roo.HtmlEditorCore.cblack= [
25809       //  'font-size' -- this can be set by the project 
25810 ];
25811
25812
25813 Roo.HtmlEditorCore.swapCodes   =[ 
25814     [    8211, "--" ], 
25815     [    8212, "--" ], 
25816     [    8216,  "'" ],  
25817     [    8217, "'" ],  
25818     [    8220, '"' ],  
25819     [    8221, '"' ],  
25820     [    8226, "*" ],  
25821     [    8230, "..." ]
25822 ]; 
25823
25824     /*
25825  * - LGPL
25826  *
25827  * HtmlEditor
25828  * 
25829  */
25830
25831 /**
25832  * @class Roo.bootstrap.HtmlEditor
25833  * @extends Roo.bootstrap.TextArea
25834  * Bootstrap HtmlEditor class
25835
25836  * @constructor
25837  * Create a new HtmlEditor
25838  * @param {Object} config The config object
25839  */
25840
25841 Roo.bootstrap.HtmlEditor = function(config){
25842     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25843     if (!this.toolbars) {
25844         this.toolbars = [];
25845     }
25846     
25847     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25848     this.addEvents({
25849             /**
25850              * @event initialize
25851              * Fires when the editor is fully initialized (including the iframe)
25852              * @param {HtmlEditor} this
25853              */
25854             initialize: true,
25855             /**
25856              * @event activate
25857              * Fires when the editor is first receives the focus. Any insertion must wait
25858              * until after this event.
25859              * @param {HtmlEditor} this
25860              */
25861             activate: true,
25862              /**
25863              * @event beforesync
25864              * Fires before the textarea is updated with content from the editor iframe. Return false
25865              * to cancel the sync.
25866              * @param {HtmlEditor} this
25867              * @param {String} html
25868              */
25869             beforesync: true,
25870              /**
25871              * @event beforepush
25872              * Fires before the iframe editor is updated with content from the textarea. Return false
25873              * to cancel the push.
25874              * @param {HtmlEditor} this
25875              * @param {String} html
25876              */
25877             beforepush: true,
25878              /**
25879              * @event sync
25880              * Fires when the textarea is updated with content from the editor iframe.
25881              * @param {HtmlEditor} this
25882              * @param {String} html
25883              */
25884             sync: true,
25885              /**
25886              * @event push
25887              * Fires when the iframe editor is updated with content from the textarea.
25888              * @param {HtmlEditor} this
25889              * @param {String} html
25890              */
25891             push: true,
25892              /**
25893              * @event editmodechange
25894              * Fires when the editor switches edit modes
25895              * @param {HtmlEditor} this
25896              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25897              */
25898             editmodechange: true,
25899             /**
25900              * @event editorevent
25901              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25902              * @param {HtmlEditor} this
25903              */
25904             editorevent: true,
25905             /**
25906              * @event firstfocus
25907              * Fires when on first focus - needed by toolbars..
25908              * @param {HtmlEditor} this
25909              */
25910             firstfocus: true,
25911             /**
25912              * @event autosave
25913              * Auto save the htmlEditor value as a file into Events
25914              * @param {HtmlEditor} this
25915              */
25916             autosave: true,
25917             /**
25918              * @event savedpreview
25919              * preview the saved version of htmlEditor
25920              * @param {HtmlEditor} this
25921              */
25922             savedpreview: true
25923         });
25924 };
25925
25926
25927 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25928     
25929     
25930       /**
25931      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25932      */
25933     toolbars : false,
25934     
25935      /**
25936     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25937     */
25938     btns : [],
25939    
25940      /**
25941      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25942      *                        Roo.resizable.
25943      */
25944     resizable : false,
25945      /**
25946      * @cfg {Number} height (in pixels)
25947      */   
25948     height: 300,
25949    /**
25950      * @cfg {Number} width (in pixels)
25951      */   
25952     width: false,
25953     
25954     /**
25955      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25956      * 
25957      */
25958     stylesheets: false,
25959     
25960     // id of frame..
25961     frameId: false,
25962     
25963     // private properties
25964     validationEvent : false,
25965     deferHeight: true,
25966     initialized : false,
25967     activated : false,
25968     
25969     onFocus : Roo.emptyFn,
25970     iframePad:3,
25971     hideMode:'offsets',
25972     
25973     tbContainer : false,
25974     
25975     bodyCls : '',
25976     
25977     toolbarContainer :function() {
25978         return this.wrap.select('.x-html-editor-tb',true).first();
25979     },
25980
25981     /**
25982      * Protected method that will not generally be called directly. It
25983      * is called when the editor creates its toolbar. Override this method if you need to
25984      * add custom toolbar buttons.
25985      * @param {HtmlEditor} editor
25986      */
25987     createToolbar : function(){
25988         Roo.log('renewing');
25989         Roo.log("create toolbars");
25990         
25991         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25992         this.toolbars[0].render(this.toolbarContainer());
25993         
25994         return;
25995         
25996 //        if (!editor.toolbars || !editor.toolbars.length) {
25997 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25998 //        }
25999 //        
26000 //        for (var i =0 ; i < editor.toolbars.length;i++) {
26001 //            editor.toolbars[i] = Roo.factory(
26002 //                    typeof(editor.toolbars[i]) == 'string' ?
26003 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
26004 //                Roo.bootstrap.HtmlEditor);
26005 //            editor.toolbars[i].init(editor);
26006 //        }
26007     },
26008
26009      
26010     // private
26011     onRender : function(ct, position)
26012     {
26013        // Roo.log("Call onRender: " + this.xtype);
26014         var _t = this;
26015         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
26016       
26017         this.wrap = this.inputEl().wrap({
26018             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
26019         });
26020         
26021         this.editorcore.onRender(ct, position);
26022          
26023         if (this.resizable) {
26024             this.resizeEl = new Roo.Resizable(this.wrap, {
26025                 pinned : true,
26026                 wrap: true,
26027                 dynamic : true,
26028                 minHeight : this.height,
26029                 height: this.height,
26030                 handles : this.resizable,
26031                 width: this.width,
26032                 listeners : {
26033                     resize : function(r, w, h) {
26034                         _t.onResize(w,h); // -something
26035                     }
26036                 }
26037             });
26038             
26039         }
26040         this.createToolbar(this);
26041        
26042         
26043         if(!this.width && this.resizable){
26044             this.setSize(this.wrap.getSize());
26045         }
26046         if (this.resizeEl) {
26047             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26048             // should trigger onReize..
26049         }
26050         
26051     },
26052
26053     // private
26054     onResize : function(w, h)
26055     {
26056         Roo.log('resize: ' +w + ',' + h );
26057         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26058         var ew = false;
26059         var eh = false;
26060         
26061         if(this.inputEl() ){
26062             if(typeof w == 'number'){
26063                 var aw = w - this.wrap.getFrameWidth('lr');
26064                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26065                 ew = aw;
26066             }
26067             if(typeof h == 'number'){
26068                  var tbh = -11;  // fixme it needs to tool bar size!
26069                 for (var i =0; i < this.toolbars.length;i++) {
26070                     // fixme - ask toolbars for heights?
26071                     tbh += this.toolbars[i].el.getHeight();
26072                     //if (this.toolbars[i].footer) {
26073                     //    tbh += this.toolbars[i].footer.el.getHeight();
26074                     //}
26075                 }
26076               
26077                 
26078                 
26079                 
26080                 
26081                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26082                 ah -= 5; // knock a few pixes off for look..
26083                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26084                 var eh = ah;
26085             }
26086         }
26087         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26088         this.editorcore.onResize(ew,eh);
26089         
26090     },
26091
26092     /**
26093      * Toggles the editor between standard and source edit mode.
26094      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26095      */
26096     toggleSourceEdit : function(sourceEditMode)
26097     {
26098         this.editorcore.toggleSourceEdit(sourceEditMode);
26099         
26100         if(this.editorcore.sourceEditMode){
26101             Roo.log('editor - showing textarea');
26102             
26103 //            Roo.log('in');
26104 //            Roo.log(this.syncValue());
26105             this.syncValue();
26106             this.inputEl().removeClass(['hide', 'x-hidden']);
26107             this.inputEl().dom.removeAttribute('tabIndex');
26108             this.inputEl().focus();
26109         }else{
26110             Roo.log('editor - hiding textarea');
26111 //            Roo.log('out')
26112 //            Roo.log(this.pushValue()); 
26113             this.pushValue();
26114             
26115             this.inputEl().addClass(['hide', 'x-hidden']);
26116             this.inputEl().dom.setAttribute('tabIndex', -1);
26117             //this.deferFocus();
26118         }
26119          
26120         if(this.resizable){
26121             this.setSize(this.wrap.getSize());
26122         }
26123         
26124         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26125     },
26126  
26127     // private (for BoxComponent)
26128     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26129
26130     // private (for BoxComponent)
26131     getResizeEl : function(){
26132         return this.wrap;
26133     },
26134
26135     // private (for BoxComponent)
26136     getPositionEl : function(){
26137         return this.wrap;
26138     },
26139
26140     // private
26141     initEvents : function(){
26142         this.originalValue = this.getValue();
26143     },
26144
26145 //    /**
26146 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26147 //     * @method
26148 //     */
26149 //    markInvalid : Roo.emptyFn,
26150 //    /**
26151 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26152 //     * @method
26153 //     */
26154 //    clearInvalid : Roo.emptyFn,
26155
26156     setValue : function(v){
26157         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26158         this.editorcore.pushValue();
26159     },
26160
26161      
26162     // private
26163     deferFocus : function(){
26164         this.focus.defer(10, this);
26165     },
26166
26167     // doc'ed in Field
26168     focus : function(){
26169         this.editorcore.focus();
26170         
26171     },
26172       
26173
26174     // private
26175     onDestroy : function(){
26176         
26177         
26178         
26179         if(this.rendered){
26180             
26181             for (var i =0; i < this.toolbars.length;i++) {
26182                 // fixme - ask toolbars for heights?
26183                 this.toolbars[i].onDestroy();
26184             }
26185             
26186             this.wrap.dom.innerHTML = '';
26187             this.wrap.remove();
26188         }
26189     },
26190
26191     // private
26192     onFirstFocus : function(){
26193         //Roo.log("onFirstFocus");
26194         this.editorcore.onFirstFocus();
26195          for (var i =0; i < this.toolbars.length;i++) {
26196             this.toolbars[i].onFirstFocus();
26197         }
26198         
26199     },
26200     
26201     // private
26202     syncValue : function()
26203     {   
26204         this.editorcore.syncValue();
26205     },
26206     
26207     pushValue : function()
26208     {   
26209         this.editorcore.pushValue();
26210     }
26211      
26212     
26213     // hide stuff that is not compatible
26214     /**
26215      * @event blur
26216      * @hide
26217      */
26218     /**
26219      * @event change
26220      * @hide
26221      */
26222     /**
26223      * @event focus
26224      * @hide
26225      */
26226     /**
26227      * @event specialkey
26228      * @hide
26229      */
26230     /**
26231      * @cfg {String} fieldClass @hide
26232      */
26233     /**
26234      * @cfg {String} focusClass @hide
26235      */
26236     /**
26237      * @cfg {String} autoCreate @hide
26238      */
26239     /**
26240      * @cfg {String} inputType @hide
26241      */
26242      
26243     /**
26244      * @cfg {String} invalidText @hide
26245      */
26246     /**
26247      * @cfg {String} msgFx @hide
26248      */
26249     /**
26250      * @cfg {String} validateOnBlur @hide
26251      */
26252 });
26253  
26254     
26255    
26256    
26257    
26258       
26259 Roo.namespace('Roo.bootstrap.htmleditor');
26260 /**
26261  * @class Roo.bootstrap.HtmlEditorToolbar1
26262  * Basic Toolbar
26263  * 
26264  * @example
26265  * Usage:
26266  *
26267  new Roo.bootstrap.HtmlEditor({
26268     ....
26269     toolbars : [
26270         new Roo.bootstrap.HtmlEditorToolbar1({
26271             disable : { fonts: 1 , format: 1, ..., ... , ...],
26272             btns : [ .... ]
26273         })
26274     }
26275      
26276  * 
26277  * @cfg {Object} disable List of elements to disable..
26278  * @cfg {Array} btns List of additional buttons.
26279  * 
26280  * 
26281  * NEEDS Extra CSS? 
26282  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26283  */
26284  
26285 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26286 {
26287     
26288     Roo.apply(this, config);
26289     
26290     // default disabled, based on 'good practice'..
26291     this.disable = this.disable || {};
26292     Roo.applyIf(this.disable, {
26293         fontSize : true,
26294         colors : true,
26295         specialElements : true
26296     });
26297     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26298     
26299     this.editor = config.editor;
26300     this.editorcore = config.editor.editorcore;
26301     
26302     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26303     
26304     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26305     // dont call parent... till later.
26306 }
26307 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26308      
26309     bar : true,
26310     
26311     editor : false,
26312     editorcore : false,
26313     
26314     
26315     formats : [
26316         "p" ,  
26317         "h1","h2","h3","h4","h5","h6", 
26318         "pre", "code", 
26319         "abbr", "acronym", "address", "cite", "samp", "var",
26320         'div','span'
26321     ],
26322     
26323     onRender : function(ct, position)
26324     {
26325        // Roo.log("Call onRender: " + this.xtype);
26326         
26327        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26328        Roo.log(this.el);
26329        this.el.dom.style.marginBottom = '0';
26330        var _this = this;
26331        var editorcore = this.editorcore;
26332        var editor= this.editor;
26333        
26334        var children = [];
26335        var btn = function(id,cmd , toggle, handler, html){
26336        
26337             var  event = toggle ? 'toggle' : 'click';
26338        
26339             var a = {
26340                 size : 'sm',
26341                 xtype: 'Button',
26342                 xns: Roo.bootstrap,
26343                 //glyphicon : id,
26344                 fa: id,
26345                 cmd : id || cmd,
26346                 enableToggle:toggle !== false,
26347                 html : html || '',
26348                 pressed : toggle ? false : null,
26349                 listeners : {}
26350             };
26351             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26352                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26353             };
26354             children.push(a);
26355             return a;
26356        }
26357        
26358     //    var cb_box = function...
26359         
26360         var style = {
26361                 xtype: 'Button',
26362                 size : 'sm',
26363                 xns: Roo.bootstrap,
26364                 fa : 'font',
26365                 //html : 'submit'
26366                 menu : {
26367                     xtype: 'Menu',
26368                     xns: Roo.bootstrap,
26369                     items:  []
26370                 }
26371         };
26372         Roo.each(this.formats, function(f) {
26373             style.menu.items.push({
26374                 xtype :'MenuItem',
26375                 xns: Roo.bootstrap,
26376                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26377                 tagname : f,
26378                 listeners : {
26379                     click : function()
26380                     {
26381                         editorcore.insertTag(this.tagname);
26382                         editor.focus();
26383                     }
26384                 }
26385                 
26386             });
26387         });
26388         children.push(style);   
26389         
26390         btn('bold',false,true);
26391         btn('italic',false,true);
26392         btn('align-left', 'justifyleft',true);
26393         btn('align-center', 'justifycenter',true);
26394         btn('align-right' , 'justifyright',true);
26395         btn('link', false, false, function(btn) {
26396             //Roo.log("create link?");
26397             var url = prompt(this.createLinkText, this.defaultLinkValue);
26398             if(url && url != 'http:/'+'/'){
26399                 this.editorcore.relayCmd('createlink', url);
26400             }
26401         }),
26402         btn('list','insertunorderedlist',true);
26403         btn('pencil', false,true, function(btn){
26404                 Roo.log(this);
26405                 this.toggleSourceEdit(btn.pressed);
26406         });
26407         
26408         if (this.editor.btns.length > 0) {
26409             for (var i = 0; i<this.editor.btns.length; i++) {
26410                 children.push(this.editor.btns[i]);
26411             }
26412         }
26413         
26414         /*
26415         var cog = {
26416                 xtype: 'Button',
26417                 size : 'sm',
26418                 xns: Roo.bootstrap,
26419                 glyphicon : 'cog',
26420                 //html : 'submit'
26421                 menu : {
26422                     xtype: 'Menu',
26423                     xns: Roo.bootstrap,
26424                     items:  []
26425                 }
26426         };
26427         
26428         cog.menu.items.push({
26429             xtype :'MenuItem',
26430             xns: Roo.bootstrap,
26431             html : Clean styles,
26432             tagname : f,
26433             listeners : {
26434                 click : function()
26435                 {
26436                     editorcore.insertTag(this.tagname);
26437                     editor.focus();
26438                 }
26439             }
26440             
26441         });
26442        */
26443         
26444          
26445        this.xtype = 'NavSimplebar';
26446         
26447         for(var i=0;i< children.length;i++) {
26448             
26449             this.buttons.add(this.addxtypeChild(children[i]));
26450             
26451         }
26452         
26453         editor.on('editorevent', this.updateToolbar, this);
26454     },
26455     onBtnClick : function(id)
26456     {
26457        this.editorcore.relayCmd(id);
26458        this.editorcore.focus();
26459     },
26460     
26461     /**
26462      * Protected method that will not generally be called directly. It triggers
26463      * a toolbar update by reading the markup state of the current selection in the editor.
26464      */
26465     updateToolbar: function(){
26466
26467         if(!this.editorcore.activated){
26468             this.editor.onFirstFocus(); // is this neeed?
26469             return;
26470         }
26471
26472         var btns = this.buttons; 
26473         var doc = this.editorcore.doc;
26474         btns.get('bold').setActive(doc.queryCommandState('bold'));
26475         btns.get('italic').setActive(doc.queryCommandState('italic'));
26476         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26477         
26478         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26479         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26480         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26481         
26482         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26483         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26484          /*
26485         
26486         var ans = this.editorcore.getAllAncestors();
26487         if (this.formatCombo) {
26488             
26489             
26490             var store = this.formatCombo.store;
26491             this.formatCombo.setValue("");
26492             for (var i =0; i < ans.length;i++) {
26493                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26494                     // select it..
26495                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26496                     break;
26497                 }
26498             }
26499         }
26500         
26501         
26502         
26503         // hides menus... - so this cant be on a menu...
26504         Roo.bootstrap.MenuMgr.hideAll();
26505         */
26506         Roo.bootstrap.MenuMgr.hideAll();
26507         //this.editorsyncValue();
26508     },
26509     onFirstFocus: function() {
26510         this.buttons.each(function(item){
26511            item.enable();
26512         });
26513     },
26514     toggleSourceEdit : function(sourceEditMode){
26515         
26516           
26517         if(sourceEditMode){
26518             Roo.log("disabling buttons");
26519            this.buttons.each( function(item){
26520                 if(item.cmd != 'pencil'){
26521                     item.disable();
26522                 }
26523             });
26524           
26525         }else{
26526             Roo.log("enabling buttons");
26527             if(this.editorcore.initialized){
26528                 this.buttons.each( function(item){
26529                     item.enable();
26530                 });
26531             }
26532             
26533         }
26534         Roo.log("calling toggole on editor");
26535         // tell the editor that it's been pressed..
26536         this.editor.toggleSourceEdit(sourceEditMode);
26537        
26538     }
26539 });
26540
26541
26542
26543
26544  
26545 /*
26546  * - LGPL
26547  */
26548
26549 /**
26550  * @class Roo.bootstrap.Markdown
26551  * @extends Roo.bootstrap.TextArea
26552  * Bootstrap Showdown editable area
26553  * @cfg {string} content
26554  * 
26555  * @constructor
26556  * Create a new Showdown
26557  */
26558
26559 Roo.bootstrap.Markdown = function(config){
26560     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26561    
26562 };
26563
26564 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26565     
26566     editing :false,
26567     
26568     initEvents : function()
26569     {
26570         
26571         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26572         this.markdownEl = this.el.createChild({
26573             cls : 'roo-markdown-area'
26574         });
26575         this.inputEl().addClass('d-none');
26576         if (this.getValue() == '') {
26577             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26578             
26579         } else {
26580             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26581         }
26582         this.markdownEl.on('click', this.toggleTextEdit, this);
26583         this.on('blur', this.toggleTextEdit, this);
26584         this.on('specialkey', this.resizeTextArea, this);
26585     },
26586     
26587     toggleTextEdit : function()
26588     {
26589         var sh = this.markdownEl.getHeight();
26590         this.inputEl().addClass('d-none');
26591         this.markdownEl.addClass('d-none');
26592         if (!this.editing) {
26593             // show editor?
26594             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26595             this.inputEl().removeClass('d-none');
26596             this.inputEl().focus();
26597             this.editing = true;
26598             return;
26599         }
26600         // show showdown...
26601         this.updateMarkdown();
26602         this.markdownEl.removeClass('d-none');
26603         this.editing = false;
26604         return;
26605     },
26606     updateMarkdown : function()
26607     {
26608         if (this.getValue() == '') {
26609             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26610             return;
26611         }
26612  
26613         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26614     },
26615     
26616     resizeTextArea: function () {
26617         
26618         var sh = 100;
26619         Roo.log([sh, this.getValue().split("\n").length * 30]);
26620         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26621     },
26622     setValue : function(val)
26623     {
26624         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26625         if (!this.editing) {
26626             this.updateMarkdown();
26627         }
26628         
26629     },
26630     focus : function()
26631     {
26632         if (!this.editing) {
26633             this.toggleTextEdit();
26634         }
26635         
26636     }
26637
26638
26639 });
26640 /**
26641  * @class Roo.bootstrap.Table.AbstractSelectionModel
26642  * @extends Roo.util.Observable
26643  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26644  * implemented by descendant classes.  This class should not be directly instantiated.
26645  * @constructor
26646  */
26647 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26648     this.locked = false;
26649     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26650 };
26651
26652
26653 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26654     /** @ignore Called by the grid automatically. Do not call directly. */
26655     init : function(grid){
26656         this.grid = grid;
26657         this.initEvents();
26658     },
26659
26660     /**
26661      * Locks the selections.
26662      */
26663     lock : function(){
26664         this.locked = true;
26665     },
26666
26667     /**
26668      * Unlocks the selections.
26669      */
26670     unlock : function(){
26671         this.locked = false;
26672     },
26673
26674     /**
26675      * Returns true if the selections are locked.
26676      * @return {Boolean}
26677      */
26678     isLocked : function(){
26679         return this.locked;
26680     },
26681     
26682     
26683     initEvents : function ()
26684     {
26685         
26686     }
26687 });
26688 /**
26689  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26690  * @class Roo.bootstrap.Table.RowSelectionModel
26691  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26692  * It supports multiple selections and keyboard selection/navigation. 
26693  * @constructor
26694  * @param {Object} config
26695  */
26696
26697 Roo.bootstrap.Table.RowSelectionModel = function(config){
26698     Roo.apply(this, config);
26699     this.selections = new Roo.util.MixedCollection(false, function(o){
26700         return o.id;
26701     });
26702
26703     this.last = false;
26704     this.lastActive = false;
26705
26706     this.addEvents({
26707         /**
26708              * @event selectionchange
26709              * Fires when the selection changes
26710              * @param {SelectionModel} this
26711              */
26712             "selectionchange" : true,
26713         /**
26714              * @event afterselectionchange
26715              * Fires after the selection changes (eg. by key press or clicking)
26716              * @param {SelectionModel} this
26717              */
26718             "afterselectionchange" : true,
26719         /**
26720              * @event beforerowselect
26721              * Fires when a row is selected being selected, return false to cancel.
26722              * @param {SelectionModel} this
26723              * @param {Number} rowIndex The selected index
26724              * @param {Boolean} keepExisting False if other selections will be cleared
26725              */
26726             "beforerowselect" : true,
26727         /**
26728              * @event rowselect
26729              * Fires when a row is selected.
26730              * @param {SelectionModel} this
26731              * @param {Number} rowIndex The selected index
26732              * @param {Roo.data.Record} r The record
26733              */
26734             "rowselect" : true,
26735         /**
26736              * @event rowdeselect
26737              * Fires when a row is deselected.
26738              * @param {SelectionModel} this
26739              * @param {Number} rowIndex The selected index
26740              */
26741         "rowdeselect" : true
26742     });
26743     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26744     this.locked = false;
26745  };
26746
26747 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26748     /**
26749      * @cfg {Boolean} singleSelect
26750      * True to allow selection of only one row at a time (defaults to false)
26751      */
26752     singleSelect : false,
26753
26754     // private
26755     initEvents : function()
26756     {
26757
26758         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26759         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26760         //}else{ // allow click to work like normal
26761          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26762         //}
26763         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26764         this.grid.on("rowclick", this.handleMouseDown, this);
26765         
26766         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26767             "up" : function(e){
26768                 if(!e.shiftKey){
26769                     this.selectPrevious(e.shiftKey);
26770                 }else if(this.last !== false && this.lastActive !== false){
26771                     var last = this.last;
26772                     this.selectRange(this.last,  this.lastActive-1);
26773                     this.grid.getView().focusRow(this.lastActive);
26774                     if(last !== false){
26775                         this.last = last;
26776                     }
26777                 }else{
26778                     this.selectFirstRow();
26779                 }
26780                 this.fireEvent("afterselectionchange", this);
26781             },
26782             "down" : function(e){
26783                 if(!e.shiftKey){
26784                     this.selectNext(e.shiftKey);
26785                 }else if(this.last !== false && this.lastActive !== false){
26786                     var last = this.last;
26787                     this.selectRange(this.last,  this.lastActive+1);
26788                     this.grid.getView().focusRow(this.lastActive);
26789                     if(last !== false){
26790                         this.last = last;
26791                     }
26792                 }else{
26793                     this.selectFirstRow();
26794                 }
26795                 this.fireEvent("afterselectionchange", this);
26796             },
26797             scope: this
26798         });
26799         this.grid.store.on('load', function(){
26800             this.selections.clear();
26801         },this);
26802         /*
26803         var view = this.grid.view;
26804         view.on("refresh", this.onRefresh, this);
26805         view.on("rowupdated", this.onRowUpdated, this);
26806         view.on("rowremoved", this.onRemove, this);
26807         */
26808     },
26809
26810     // private
26811     onRefresh : function()
26812     {
26813         var ds = this.grid.store, i, v = this.grid.view;
26814         var s = this.selections;
26815         s.each(function(r){
26816             if((i = ds.indexOfId(r.id)) != -1){
26817                 v.onRowSelect(i);
26818             }else{
26819                 s.remove(r);
26820             }
26821         });
26822     },
26823
26824     // private
26825     onRemove : function(v, index, r){
26826         this.selections.remove(r);
26827     },
26828
26829     // private
26830     onRowUpdated : function(v, index, r){
26831         if(this.isSelected(r)){
26832             v.onRowSelect(index);
26833         }
26834     },
26835
26836     /**
26837      * Select records.
26838      * @param {Array} records The records to select
26839      * @param {Boolean} keepExisting (optional) True to keep existing selections
26840      */
26841     selectRecords : function(records, keepExisting)
26842     {
26843         if(!keepExisting){
26844             this.clearSelections();
26845         }
26846             var ds = this.grid.store;
26847         for(var i = 0, len = records.length; i < len; i++){
26848             this.selectRow(ds.indexOf(records[i]), true);
26849         }
26850     },
26851
26852     /**
26853      * Gets the number of selected rows.
26854      * @return {Number}
26855      */
26856     getCount : function(){
26857         return this.selections.length;
26858     },
26859
26860     /**
26861      * Selects the first row in the grid.
26862      */
26863     selectFirstRow : function(){
26864         this.selectRow(0);
26865     },
26866
26867     /**
26868      * Select the last row.
26869      * @param {Boolean} keepExisting (optional) True to keep existing selections
26870      */
26871     selectLastRow : function(keepExisting){
26872         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26873         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26874     },
26875
26876     /**
26877      * Selects the row immediately following the last selected row.
26878      * @param {Boolean} keepExisting (optional) True to keep existing selections
26879      */
26880     selectNext : function(keepExisting)
26881     {
26882             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26883             this.selectRow(this.last+1, keepExisting);
26884             this.grid.getView().focusRow(this.last);
26885         }
26886     },
26887
26888     /**
26889      * Selects the row that precedes the last selected row.
26890      * @param {Boolean} keepExisting (optional) True to keep existing selections
26891      */
26892     selectPrevious : function(keepExisting){
26893         if(this.last){
26894             this.selectRow(this.last-1, keepExisting);
26895             this.grid.getView().focusRow(this.last);
26896         }
26897     },
26898
26899     /**
26900      * Returns the selected records
26901      * @return {Array} Array of selected records
26902      */
26903     getSelections : function(){
26904         return [].concat(this.selections.items);
26905     },
26906
26907     /**
26908      * Returns the first selected record.
26909      * @return {Record}
26910      */
26911     getSelected : function(){
26912         return this.selections.itemAt(0);
26913     },
26914
26915
26916     /**
26917      * Clears all selections.
26918      */
26919     clearSelections : function(fast)
26920     {
26921         if(this.locked) {
26922             return;
26923         }
26924         if(fast !== true){
26925                 var ds = this.grid.store;
26926             var s = this.selections;
26927             s.each(function(r){
26928                 this.deselectRow(ds.indexOfId(r.id));
26929             }, this);
26930             s.clear();
26931         }else{
26932             this.selections.clear();
26933         }
26934         this.last = false;
26935     },
26936
26937
26938     /**
26939      * Selects all rows.
26940      */
26941     selectAll : function(){
26942         if(this.locked) {
26943             return;
26944         }
26945         this.selections.clear();
26946         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26947             this.selectRow(i, true);
26948         }
26949     },
26950
26951     /**
26952      * Returns True if there is a selection.
26953      * @return {Boolean}
26954      */
26955     hasSelection : function(){
26956         return this.selections.length > 0;
26957     },
26958
26959     /**
26960      * Returns True if the specified row is selected.
26961      * @param {Number/Record} record The record or index of the record to check
26962      * @return {Boolean}
26963      */
26964     isSelected : function(index){
26965             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26966         return (r && this.selections.key(r.id) ? true : false);
26967     },
26968
26969     /**
26970      * Returns True if the specified record id is selected.
26971      * @param {String} id The id of record to check
26972      * @return {Boolean}
26973      */
26974     isIdSelected : function(id){
26975         return (this.selections.key(id) ? true : false);
26976     },
26977
26978
26979     // private
26980     handleMouseDBClick : function(e, t){
26981         
26982     },
26983     // private
26984     handleMouseDown : function(e, t)
26985     {
26986             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26987         if(this.isLocked() || rowIndex < 0 ){
26988             return;
26989         };
26990         if(e.shiftKey && this.last !== false){
26991             var last = this.last;
26992             this.selectRange(last, rowIndex, e.ctrlKey);
26993             this.last = last; // reset the last
26994             t.focus();
26995     
26996         }else{
26997             var isSelected = this.isSelected(rowIndex);
26998             //Roo.log("select row:" + rowIndex);
26999             if(isSelected){
27000                 this.deselectRow(rowIndex);
27001             } else {
27002                         this.selectRow(rowIndex, true);
27003             }
27004     
27005             /*
27006                 if(e.button !== 0 && isSelected){
27007                 alert('rowIndex 2: ' + rowIndex);
27008                     view.focusRow(rowIndex);
27009                 }else if(e.ctrlKey && isSelected){
27010                     this.deselectRow(rowIndex);
27011                 }else if(!isSelected){
27012                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
27013                     view.focusRow(rowIndex);
27014                 }
27015             */
27016         }
27017         this.fireEvent("afterselectionchange", this);
27018     },
27019     // private
27020     handleDragableRowClick :  function(grid, rowIndex, e) 
27021     {
27022         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27023             this.selectRow(rowIndex, false);
27024             grid.view.focusRow(rowIndex);
27025              this.fireEvent("afterselectionchange", this);
27026         }
27027     },
27028     
27029     /**
27030      * Selects multiple rows.
27031      * @param {Array} rows Array of the indexes of the row to select
27032      * @param {Boolean} keepExisting (optional) True to keep existing selections
27033      */
27034     selectRows : function(rows, keepExisting){
27035         if(!keepExisting){
27036             this.clearSelections();
27037         }
27038         for(var i = 0, len = rows.length; i < len; i++){
27039             this.selectRow(rows[i], true);
27040         }
27041     },
27042
27043     /**
27044      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27045      * @param {Number} startRow The index of the first row in the range
27046      * @param {Number} endRow The index of the last row in the range
27047      * @param {Boolean} keepExisting (optional) True to retain existing selections
27048      */
27049     selectRange : function(startRow, endRow, keepExisting){
27050         if(this.locked) {
27051             return;
27052         }
27053         if(!keepExisting){
27054             this.clearSelections();
27055         }
27056         if(startRow <= endRow){
27057             for(var i = startRow; i <= endRow; i++){
27058                 this.selectRow(i, true);
27059             }
27060         }else{
27061             for(var i = startRow; i >= endRow; i--){
27062                 this.selectRow(i, true);
27063             }
27064         }
27065     },
27066
27067     /**
27068      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27069      * @param {Number} startRow The index of the first row in the range
27070      * @param {Number} endRow The index of the last row in the range
27071      */
27072     deselectRange : function(startRow, endRow, preventViewNotify){
27073         if(this.locked) {
27074             return;
27075         }
27076         for(var i = startRow; i <= endRow; i++){
27077             this.deselectRow(i, preventViewNotify);
27078         }
27079     },
27080
27081     /**
27082      * Selects a row.
27083      * @param {Number} row The index of the row to select
27084      * @param {Boolean} keepExisting (optional) True to keep existing selections
27085      */
27086     selectRow : function(index, keepExisting, preventViewNotify)
27087     {
27088             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27089             return;
27090         }
27091         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27092             if(!keepExisting || this.singleSelect){
27093                 this.clearSelections();
27094             }
27095             
27096             var r = this.grid.store.getAt(index);
27097             //console.log('selectRow - record id :' + r.id);
27098             
27099             this.selections.add(r);
27100             this.last = this.lastActive = index;
27101             if(!preventViewNotify){
27102                 var proxy = new Roo.Element(
27103                                 this.grid.getRowDom(index)
27104                 );
27105                 proxy.addClass('bg-info info');
27106             }
27107             this.fireEvent("rowselect", this, index, r);
27108             this.fireEvent("selectionchange", this);
27109         }
27110     },
27111
27112     /**
27113      * Deselects a row.
27114      * @param {Number} row The index of the row to deselect
27115      */
27116     deselectRow : function(index, preventViewNotify)
27117     {
27118         if(this.locked) {
27119             return;
27120         }
27121         if(this.last == index){
27122             this.last = false;
27123         }
27124         if(this.lastActive == index){
27125             this.lastActive = false;
27126         }
27127         
27128         var r = this.grid.store.getAt(index);
27129         if (!r) {
27130             return;
27131         }
27132         
27133         this.selections.remove(r);
27134         //.console.log('deselectRow - record id :' + r.id);
27135         if(!preventViewNotify){
27136         
27137             var proxy = new Roo.Element(
27138                 this.grid.getRowDom(index)
27139             );
27140             proxy.removeClass('bg-info info');
27141         }
27142         this.fireEvent("rowdeselect", this, index);
27143         this.fireEvent("selectionchange", this);
27144     },
27145
27146     // private
27147     restoreLast : function(){
27148         if(this._last){
27149             this.last = this._last;
27150         }
27151     },
27152
27153     // private
27154     acceptsNav : function(row, col, cm){
27155         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27156     },
27157
27158     // private
27159     onEditorKey : function(field, e){
27160         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27161         if(k == e.TAB){
27162             e.stopEvent();
27163             ed.completeEdit();
27164             if(e.shiftKey){
27165                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27166             }else{
27167                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27168             }
27169         }else if(k == e.ENTER && !e.ctrlKey){
27170             e.stopEvent();
27171             ed.completeEdit();
27172             if(e.shiftKey){
27173                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27174             }else{
27175                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27176             }
27177         }else if(k == e.ESC){
27178             ed.cancelEdit();
27179         }
27180         if(newCell){
27181             g.startEditing(newCell[0], newCell[1]);
27182         }
27183     }
27184 });
27185 /*
27186  * Based on:
27187  * Ext JS Library 1.1.1
27188  * Copyright(c) 2006-2007, Ext JS, LLC.
27189  *
27190  * Originally Released Under LGPL - original licence link has changed is not relivant.
27191  *
27192  * Fork - LGPL
27193  * <script type="text/javascript">
27194  */
27195  
27196 /**
27197  * @class Roo.bootstrap.PagingToolbar
27198  * @extends Roo.bootstrap.NavSimplebar
27199  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27200  * @constructor
27201  * Create a new PagingToolbar
27202  * @param {Object} config The config object
27203  * @param {Roo.data.Store} store
27204  */
27205 Roo.bootstrap.PagingToolbar = function(config)
27206 {
27207     // old args format still supported... - xtype is prefered..
27208         // created from xtype...
27209     
27210     this.ds = config.dataSource;
27211     
27212     if (config.store && !this.ds) {
27213         this.store= Roo.factory(config.store, Roo.data);
27214         this.ds = this.store;
27215         this.ds.xmodule = this.xmodule || false;
27216     }
27217     
27218     this.toolbarItems = [];
27219     if (config.items) {
27220         this.toolbarItems = config.items;
27221     }
27222     
27223     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27224     
27225     this.cursor = 0;
27226     
27227     if (this.ds) { 
27228         this.bind(this.ds);
27229     }
27230     
27231     if (Roo.bootstrap.version == 4) {
27232         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27233     } else {
27234         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27235     }
27236     
27237 };
27238
27239 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27240     /**
27241      * @cfg {Roo.data.Store} dataSource
27242      * The underlying data store providing the paged data
27243      */
27244     /**
27245      * @cfg {String/HTMLElement/Element} container
27246      * container The id or element that will contain the toolbar
27247      */
27248     /**
27249      * @cfg {Boolean} displayInfo
27250      * True to display the displayMsg (defaults to false)
27251      */
27252     /**
27253      * @cfg {Number} pageSize
27254      * The number of records to display per page (defaults to 20)
27255      */
27256     pageSize: 20,
27257     /**
27258      * @cfg {String} displayMsg
27259      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27260      */
27261     displayMsg : 'Displaying {0} - {1} of {2}',
27262     /**
27263      * @cfg {String} emptyMsg
27264      * The message to display when no records are found (defaults to "No data to display")
27265      */
27266     emptyMsg : 'No data to display',
27267     /**
27268      * Customizable piece of the default paging text (defaults to "Page")
27269      * @type String
27270      */
27271     beforePageText : "Page",
27272     /**
27273      * Customizable piece of the default paging text (defaults to "of %0")
27274      * @type String
27275      */
27276     afterPageText : "of {0}",
27277     /**
27278      * Customizable piece of the default paging text (defaults to "First Page")
27279      * @type String
27280      */
27281     firstText : "First Page",
27282     /**
27283      * Customizable piece of the default paging text (defaults to "Previous Page")
27284      * @type String
27285      */
27286     prevText : "Previous Page",
27287     /**
27288      * Customizable piece of the default paging text (defaults to "Next Page")
27289      * @type String
27290      */
27291     nextText : "Next Page",
27292     /**
27293      * Customizable piece of the default paging text (defaults to "Last Page")
27294      * @type String
27295      */
27296     lastText : "Last Page",
27297     /**
27298      * Customizable piece of the default paging text (defaults to "Refresh")
27299      * @type String
27300      */
27301     refreshText : "Refresh",
27302
27303     buttons : false,
27304     // private
27305     onRender : function(ct, position) 
27306     {
27307         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27308         this.navgroup.parentId = this.id;
27309         this.navgroup.onRender(this.el, null);
27310         // add the buttons to the navgroup
27311         
27312         if(this.displayInfo){
27313             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27314             this.displayEl = this.el.select('.x-paging-info', true).first();
27315 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27316 //            this.displayEl = navel.el.select('span',true).first();
27317         }
27318         
27319         var _this = this;
27320         
27321         if(this.buttons){
27322             Roo.each(_this.buttons, function(e){ // this might need to use render????
27323                Roo.factory(e).render(_this.el);
27324             });
27325         }
27326             
27327         Roo.each(_this.toolbarItems, function(e) {
27328             _this.navgroup.addItem(e);
27329         });
27330         
27331         
27332         this.first = this.navgroup.addItem({
27333             tooltip: this.firstText,
27334             cls: "prev btn-outline-secondary",
27335             html : ' <i class="fa fa-step-backward"></i>',
27336             disabled: true,
27337             preventDefault: true,
27338             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27339         });
27340         
27341         this.prev =  this.navgroup.addItem({
27342             tooltip: this.prevText,
27343             cls: "prev btn-outline-secondary",
27344             html : ' <i class="fa fa-backward"></i>',
27345             disabled: true,
27346             preventDefault: true,
27347             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27348         });
27349     //this.addSeparator();
27350         
27351         
27352         var field = this.navgroup.addItem( {
27353             tagtype : 'span',
27354             cls : 'x-paging-position  btn-outline-secondary',
27355              disabled: true,
27356             html : this.beforePageText  +
27357                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27358                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27359          } ); //?? escaped?
27360         
27361         this.field = field.el.select('input', true).first();
27362         this.field.on("keydown", this.onPagingKeydown, this);
27363         this.field.on("focus", function(){this.dom.select();});
27364     
27365     
27366         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27367         //this.field.setHeight(18);
27368         //this.addSeparator();
27369         this.next = this.navgroup.addItem({
27370             tooltip: this.nextText,
27371             cls: "next btn-outline-secondary",
27372             html : ' <i class="fa fa-forward"></i>',
27373             disabled: true,
27374             preventDefault: true,
27375             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27376         });
27377         this.last = this.navgroup.addItem({
27378             tooltip: this.lastText,
27379             html : ' <i class="fa fa-step-forward"></i>',
27380             cls: "next btn-outline-secondary",
27381             disabled: true,
27382             preventDefault: true,
27383             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27384         });
27385     //this.addSeparator();
27386         this.loading = this.navgroup.addItem({
27387             tooltip: this.refreshText,
27388             cls: "btn-outline-secondary",
27389             html : ' <i class="fa fa-refresh"></i>',
27390             preventDefault: true,
27391             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27392         });
27393         
27394     },
27395
27396     // private
27397     updateInfo : function(){
27398         if(this.displayEl){
27399             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27400             var msg = count == 0 ?
27401                 this.emptyMsg :
27402                 String.format(
27403                     this.displayMsg,
27404                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27405                 );
27406             this.displayEl.update(msg);
27407         }
27408     },
27409
27410     // private
27411     onLoad : function(ds, r, o)
27412     {
27413         this.cursor = o.params && o.params.start ? o.params.start : 0;
27414         
27415         var d = this.getPageData(),
27416             ap = d.activePage,
27417             ps = d.pages;
27418         
27419         
27420         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27421         this.field.dom.value = ap;
27422         this.first.setDisabled(ap == 1);
27423         this.prev.setDisabled(ap == 1);
27424         this.next.setDisabled(ap == ps);
27425         this.last.setDisabled(ap == ps);
27426         this.loading.enable();
27427         this.updateInfo();
27428     },
27429
27430     // private
27431     getPageData : function(){
27432         var total = this.ds.getTotalCount();
27433         return {
27434             total : total,
27435             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27436             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27437         };
27438     },
27439
27440     // private
27441     onLoadError : function(){
27442         this.loading.enable();
27443     },
27444
27445     // private
27446     onPagingKeydown : function(e){
27447         var k = e.getKey();
27448         var d = this.getPageData();
27449         if(k == e.RETURN){
27450             var v = this.field.dom.value, pageNum;
27451             if(!v || isNaN(pageNum = parseInt(v, 10))){
27452                 this.field.dom.value = d.activePage;
27453                 return;
27454             }
27455             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27456             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27457             e.stopEvent();
27458         }
27459         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))
27460         {
27461           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27462           this.field.dom.value = pageNum;
27463           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27464           e.stopEvent();
27465         }
27466         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27467         {
27468           var v = this.field.dom.value, pageNum; 
27469           var increment = (e.shiftKey) ? 10 : 1;
27470           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27471                 increment *= -1;
27472           }
27473           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27474             this.field.dom.value = d.activePage;
27475             return;
27476           }
27477           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27478           {
27479             this.field.dom.value = parseInt(v, 10) + increment;
27480             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27481             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27482           }
27483           e.stopEvent();
27484         }
27485     },
27486
27487     // private
27488     beforeLoad : function(){
27489         if(this.loading){
27490             this.loading.disable();
27491         }
27492     },
27493
27494     // private
27495     onClick : function(which){
27496         
27497         var ds = this.ds;
27498         if (!ds) {
27499             return;
27500         }
27501         
27502         switch(which){
27503             case "first":
27504                 ds.load({params:{start: 0, limit: this.pageSize}});
27505             break;
27506             case "prev":
27507                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27508             break;
27509             case "next":
27510                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27511             break;
27512             case "last":
27513                 var total = ds.getTotalCount();
27514                 var extra = total % this.pageSize;
27515                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27516                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27517             break;
27518             case "refresh":
27519                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27520             break;
27521         }
27522     },
27523
27524     /**
27525      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27526      * @param {Roo.data.Store} store The data store to unbind
27527      */
27528     unbind : function(ds){
27529         ds.un("beforeload", this.beforeLoad, this);
27530         ds.un("load", this.onLoad, this);
27531         ds.un("loadexception", this.onLoadError, this);
27532         ds.un("remove", this.updateInfo, this);
27533         ds.un("add", this.updateInfo, this);
27534         this.ds = undefined;
27535     },
27536
27537     /**
27538      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27539      * @param {Roo.data.Store} store The data store to bind
27540      */
27541     bind : function(ds){
27542         ds.on("beforeload", this.beforeLoad, this);
27543         ds.on("load", this.onLoad, this);
27544         ds.on("loadexception", this.onLoadError, this);
27545         ds.on("remove", this.updateInfo, this);
27546         ds.on("add", this.updateInfo, this);
27547         this.ds = ds;
27548     }
27549 });/*
27550  * - LGPL
27551  *
27552  * element
27553  * 
27554  */
27555
27556 /**
27557  * @class Roo.bootstrap.MessageBar
27558  * @extends Roo.bootstrap.Component
27559  * Bootstrap MessageBar class
27560  * @cfg {String} html contents of the MessageBar
27561  * @cfg {String} weight (info | success | warning | danger) default info
27562  * @cfg {String} beforeClass insert the bar before the given class
27563  * @cfg {Boolean} closable (true | false) default false
27564  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27565  * 
27566  * @constructor
27567  * Create a new Element
27568  * @param {Object} config The config object
27569  */
27570
27571 Roo.bootstrap.MessageBar = function(config){
27572     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27573 };
27574
27575 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27576     
27577     html: '',
27578     weight: 'info',
27579     closable: false,
27580     fixed: false,
27581     beforeClass: 'bootstrap-sticky-wrap',
27582     
27583     getAutoCreate : function(){
27584         
27585         var cfg = {
27586             tag: 'div',
27587             cls: 'alert alert-dismissable alert-' + this.weight,
27588             cn: [
27589                 {
27590                     tag: 'span',
27591                     cls: 'message',
27592                     html: this.html || ''
27593                 }
27594             ]
27595         };
27596         
27597         if(this.fixed){
27598             cfg.cls += ' alert-messages-fixed';
27599         }
27600         
27601         if(this.closable){
27602             cfg.cn.push({
27603                 tag: 'button',
27604                 cls: 'close',
27605                 html: 'x'
27606             });
27607         }
27608         
27609         return cfg;
27610     },
27611     
27612     onRender : function(ct, position)
27613     {
27614         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27615         
27616         if(!this.el){
27617             var cfg = Roo.apply({},  this.getAutoCreate());
27618             cfg.id = Roo.id();
27619             
27620             if (this.cls) {
27621                 cfg.cls += ' ' + this.cls;
27622             }
27623             if (this.style) {
27624                 cfg.style = this.style;
27625             }
27626             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27627             
27628             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27629         }
27630         
27631         this.el.select('>button.close').on('click', this.hide, this);
27632         
27633     },
27634     
27635     show : function()
27636     {
27637         if (!this.rendered) {
27638             this.render();
27639         }
27640         
27641         this.el.show();
27642         
27643         this.fireEvent('show', this);
27644         
27645     },
27646     
27647     hide : function()
27648     {
27649         if (!this.rendered) {
27650             this.render();
27651         }
27652         
27653         this.el.hide();
27654         
27655         this.fireEvent('hide', this);
27656     },
27657     
27658     update : function()
27659     {
27660 //        var e = this.el.dom.firstChild;
27661 //        
27662 //        if(this.closable){
27663 //            e = e.nextSibling;
27664 //        }
27665 //        
27666 //        e.data = this.html || '';
27667
27668         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27669     }
27670    
27671 });
27672
27673  
27674
27675      /*
27676  * - LGPL
27677  *
27678  * Graph
27679  * 
27680  */
27681
27682
27683 /**
27684  * @class Roo.bootstrap.Graph
27685  * @extends Roo.bootstrap.Component
27686  * Bootstrap Graph class
27687 > Prameters
27688  -sm {number} sm 4
27689  -md {number} md 5
27690  @cfg {String} graphtype  bar | vbar | pie
27691  @cfg {number} g_x coodinator | centre x (pie)
27692  @cfg {number} g_y coodinator | centre y (pie)
27693  @cfg {number} g_r radius (pie)
27694  @cfg {number} g_height height of the chart (respected by all elements in the set)
27695  @cfg {number} g_width width of the chart (respected by all elements in the set)
27696  @cfg {Object} title The title of the chart
27697     
27698  -{Array}  values
27699  -opts (object) options for the chart 
27700      o {
27701      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27702      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27703      o vgutter (number)
27704      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.
27705      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27706      o to
27707      o stretch (boolean)
27708      o }
27709  -opts (object) options for the pie
27710      o{
27711      o cut
27712      o startAngle (number)
27713      o endAngle (number)
27714      } 
27715  *
27716  * @constructor
27717  * Create a new Input
27718  * @param {Object} config The config object
27719  */
27720
27721 Roo.bootstrap.Graph = function(config){
27722     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27723     
27724     this.addEvents({
27725         // img events
27726         /**
27727          * @event click
27728          * The img click event for the img.
27729          * @param {Roo.EventObject} e
27730          */
27731         "click" : true
27732     });
27733 };
27734
27735 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27736     
27737     sm: 4,
27738     md: 5,
27739     graphtype: 'bar',
27740     g_height: 250,
27741     g_width: 400,
27742     g_x: 50,
27743     g_y: 50,
27744     g_r: 30,
27745     opts:{
27746         //g_colors: this.colors,
27747         g_type: 'soft',
27748         g_gutter: '20%'
27749
27750     },
27751     title : false,
27752
27753     getAutoCreate : function(){
27754         
27755         var cfg = {
27756             tag: 'div',
27757             html : null
27758         };
27759         
27760         
27761         return  cfg;
27762     },
27763
27764     onRender : function(ct,position){
27765         
27766         
27767         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27768         
27769         if (typeof(Raphael) == 'undefined') {
27770             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27771             return;
27772         }
27773         
27774         this.raphael = Raphael(this.el.dom);
27775         
27776                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27777                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27778                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27779                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27780                 /*
27781                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27782                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27783                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27784                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27785                 
27786                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27787                 r.barchart(330, 10, 300, 220, data1);
27788                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27789                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27790                 */
27791                 
27792                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27793                 // r.barchart(30, 30, 560, 250,  xdata, {
27794                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27795                 //     axis : "0 0 1 1",
27796                 //     axisxlabels :  xdata
27797                 //     //yvalues : cols,
27798                    
27799                 // });
27800 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27801 //        
27802 //        this.load(null,xdata,{
27803 //                axis : "0 0 1 1",
27804 //                axisxlabels :  xdata
27805 //                });
27806
27807     },
27808
27809     load : function(graphtype,xdata,opts)
27810     {
27811         this.raphael.clear();
27812         if(!graphtype) {
27813             graphtype = this.graphtype;
27814         }
27815         if(!opts){
27816             opts = this.opts;
27817         }
27818         var r = this.raphael,
27819             fin = function () {
27820                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27821             },
27822             fout = function () {
27823                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27824             },
27825             pfin = function() {
27826                 this.sector.stop();
27827                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27828
27829                 if (this.label) {
27830                     this.label[0].stop();
27831                     this.label[0].attr({ r: 7.5 });
27832                     this.label[1].attr({ "font-weight": 800 });
27833                 }
27834             },
27835             pfout = function() {
27836                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27837
27838                 if (this.label) {
27839                     this.label[0].animate({ r: 5 }, 500, "bounce");
27840                     this.label[1].attr({ "font-weight": 400 });
27841                 }
27842             };
27843
27844         switch(graphtype){
27845             case 'bar':
27846                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27847                 break;
27848             case 'hbar':
27849                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27850                 break;
27851             case 'pie':
27852 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27853 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27854 //            
27855                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27856                 
27857                 break;
27858
27859         }
27860         
27861         if(this.title){
27862             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27863         }
27864         
27865     },
27866     
27867     setTitle: function(o)
27868     {
27869         this.title = o;
27870     },
27871     
27872     initEvents: function() {
27873         
27874         if(!this.href){
27875             this.el.on('click', this.onClick, this);
27876         }
27877     },
27878     
27879     onClick : function(e)
27880     {
27881         Roo.log('img onclick');
27882         this.fireEvent('click', this, e);
27883     }
27884    
27885 });
27886
27887  
27888 /*
27889  * - LGPL
27890  *
27891  * numberBox
27892  * 
27893  */
27894 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27895
27896 /**
27897  * @class Roo.bootstrap.dash.NumberBox
27898  * @extends Roo.bootstrap.Component
27899  * Bootstrap NumberBox class
27900  * @cfg {String} headline Box headline
27901  * @cfg {String} content Box content
27902  * @cfg {String} icon Box icon
27903  * @cfg {String} footer Footer text
27904  * @cfg {String} fhref Footer href
27905  * 
27906  * @constructor
27907  * Create a new NumberBox
27908  * @param {Object} config The config object
27909  */
27910
27911
27912 Roo.bootstrap.dash.NumberBox = function(config){
27913     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27914     
27915 };
27916
27917 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27918     
27919     headline : '',
27920     content : '',
27921     icon : '',
27922     footer : '',
27923     fhref : '',
27924     ficon : '',
27925     
27926     getAutoCreate : function(){
27927         
27928         var cfg = {
27929             tag : 'div',
27930             cls : 'small-box ',
27931             cn : [
27932                 {
27933                     tag : 'div',
27934                     cls : 'inner',
27935                     cn :[
27936                         {
27937                             tag : 'h3',
27938                             cls : 'roo-headline',
27939                             html : this.headline
27940                         },
27941                         {
27942                             tag : 'p',
27943                             cls : 'roo-content',
27944                             html : this.content
27945                         }
27946                     ]
27947                 }
27948             ]
27949         };
27950         
27951         if(this.icon){
27952             cfg.cn.push({
27953                 tag : 'div',
27954                 cls : 'icon',
27955                 cn :[
27956                     {
27957                         tag : 'i',
27958                         cls : 'ion ' + this.icon
27959                     }
27960                 ]
27961             });
27962         }
27963         
27964         if(this.footer){
27965             var footer = {
27966                 tag : 'a',
27967                 cls : 'small-box-footer',
27968                 href : this.fhref || '#',
27969                 html : this.footer
27970             };
27971             
27972             cfg.cn.push(footer);
27973             
27974         }
27975         
27976         return  cfg;
27977     },
27978
27979     onRender : function(ct,position){
27980         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27981
27982
27983        
27984                 
27985     },
27986
27987     setHeadline: function (value)
27988     {
27989         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27990     },
27991     
27992     setFooter: function (value, href)
27993     {
27994         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27995         
27996         if(href){
27997             this.el.select('a.small-box-footer',true).first().attr('href', href);
27998         }
27999         
28000     },
28001
28002     setContent: function (value)
28003     {
28004         this.el.select('.roo-content',true).first().dom.innerHTML = value;
28005     },
28006
28007     initEvents: function() 
28008     {   
28009         
28010     }
28011     
28012 });
28013
28014  
28015 /*
28016  * - LGPL
28017  *
28018  * TabBox
28019  * 
28020  */
28021 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28022
28023 /**
28024  * @class Roo.bootstrap.dash.TabBox
28025  * @extends Roo.bootstrap.Component
28026  * Bootstrap TabBox class
28027  * @cfg {String} title Title of the TabBox
28028  * @cfg {String} icon Icon of the TabBox
28029  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28030  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28031  * 
28032  * @constructor
28033  * Create a new TabBox
28034  * @param {Object} config The config object
28035  */
28036
28037
28038 Roo.bootstrap.dash.TabBox = function(config){
28039     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28040     this.addEvents({
28041         // raw events
28042         /**
28043          * @event addpane
28044          * When a pane is added
28045          * @param {Roo.bootstrap.dash.TabPane} pane
28046          */
28047         "addpane" : true,
28048         /**
28049          * @event activatepane
28050          * When a pane is activated
28051          * @param {Roo.bootstrap.dash.TabPane} pane
28052          */
28053         "activatepane" : true
28054         
28055          
28056     });
28057     
28058     this.panes = [];
28059 };
28060
28061 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28062
28063     title : '',
28064     icon : false,
28065     showtabs : true,
28066     tabScrollable : false,
28067     
28068     getChildContainer : function()
28069     {
28070         return this.el.select('.tab-content', true).first();
28071     },
28072     
28073     getAutoCreate : function(){
28074         
28075         var header = {
28076             tag: 'li',
28077             cls: 'pull-left header',
28078             html: this.title,
28079             cn : []
28080         };
28081         
28082         if(this.icon){
28083             header.cn.push({
28084                 tag: 'i',
28085                 cls: 'fa ' + this.icon
28086             });
28087         }
28088         
28089         var h = {
28090             tag: 'ul',
28091             cls: 'nav nav-tabs pull-right',
28092             cn: [
28093                 header
28094             ]
28095         };
28096         
28097         if(this.tabScrollable){
28098             h = {
28099                 tag: 'div',
28100                 cls: 'tab-header',
28101                 cn: [
28102                     {
28103                         tag: 'ul',
28104                         cls: 'nav nav-tabs pull-right',
28105                         cn: [
28106                             header
28107                         ]
28108                     }
28109                 ]
28110             };
28111         }
28112         
28113         var cfg = {
28114             tag: 'div',
28115             cls: 'nav-tabs-custom',
28116             cn: [
28117                 h,
28118                 {
28119                     tag: 'div',
28120                     cls: 'tab-content no-padding',
28121                     cn: []
28122                 }
28123             ]
28124         };
28125
28126         return  cfg;
28127     },
28128     initEvents : function()
28129     {
28130         //Roo.log('add add pane handler');
28131         this.on('addpane', this.onAddPane, this);
28132     },
28133      /**
28134      * Updates the box title
28135      * @param {String} html to set the title to.
28136      */
28137     setTitle : function(value)
28138     {
28139         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28140     },
28141     onAddPane : function(pane)
28142     {
28143         this.panes.push(pane);
28144         //Roo.log('addpane');
28145         //Roo.log(pane);
28146         // tabs are rendere left to right..
28147         if(!this.showtabs){
28148             return;
28149         }
28150         
28151         var ctr = this.el.select('.nav-tabs', true).first();
28152          
28153          
28154         var existing = ctr.select('.nav-tab',true);
28155         var qty = existing.getCount();;
28156         
28157         
28158         var tab = ctr.createChild({
28159             tag : 'li',
28160             cls : 'nav-tab' + (qty ? '' : ' active'),
28161             cn : [
28162                 {
28163                     tag : 'a',
28164                     href:'#',
28165                     html : pane.title
28166                 }
28167             ]
28168         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28169         pane.tab = tab;
28170         
28171         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28172         if (!qty) {
28173             pane.el.addClass('active');
28174         }
28175         
28176                 
28177     },
28178     onTabClick : function(ev,un,ob,pane)
28179     {
28180         //Roo.log('tab - prev default');
28181         ev.preventDefault();
28182         
28183         
28184         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28185         pane.tab.addClass('active');
28186         //Roo.log(pane.title);
28187         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28188         // technically we should have a deactivate event.. but maybe add later.
28189         // and it should not de-activate the selected tab...
28190         this.fireEvent('activatepane', pane);
28191         pane.el.addClass('active');
28192         pane.fireEvent('activate');
28193         
28194         
28195     },
28196     
28197     getActivePane : function()
28198     {
28199         var r = false;
28200         Roo.each(this.panes, function(p) {
28201             if(p.el.hasClass('active')){
28202                 r = p;
28203                 return false;
28204             }
28205             
28206             return;
28207         });
28208         
28209         return r;
28210     }
28211     
28212     
28213 });
28214
28215  
28216 /*
28217  * - LGPL
28218  *
28219  * Tab pane
28220  * 
28221  */
28222 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28223 /**
28224  * @class Roo.bootstrap.TabPane
28225  * @extends Roo.bootstrap.Component
28226  * Bootstrap TabPane class
28227  * @cfg {Boolean} active (false | true) Default false
28228  * @cfg {String} title title of panel
28229
28230  * 
28231  * @constructor
28232  * Create a new TabPane
28233  * @param {Object} config The config object
28234  */
28235
28236 Roo.bootstrap.dash.TabPane = function(config){
28237     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28238     
28239     this.addEvents({
28240         // raw events
28241         /**
28242          * @event activate
28243          * When a pane is activated
28244          * @param {Roo.bootstrap.dash.TabPane} pane
28245          */
28246         "activate" : true
28247          
28248     });
28249 };
28250
28251 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28252     
28253     active : false,
28254     title : '',
28255     
28256     // the tabBox that this is attached to.
28257     tab : false,
28258      
28259     getAutoCreate : function() 
28260     {
28261         var cfg = {
28262             tag: 'div',
28263             cls: 'tab-pane'
28264         };
28265         
28266         if(this.active){
28267             cfg.cls += ' active';
28268         }
28269         
28270         return cfg;
28271     },
28272     initEvents  : function()
28273     {
28274         //Roo.log('trigger add pane handler');
28275         this.parent().fireEvent('addpane', this)
28276     },
28277     
28278      /**
28279      * Updates the tab title 
28280      * @param {String} html to set the title to.
28281      */
28282     setTitle: function(str)
28283     {
28284         if (!this.tab) {
28285             return;
28286         }
28287         this.title = str;
28288         this.tab.select('a', true).first().dom.innerHTML = str;
28289         
28290     }
28291     
28292     
28293     
28294 });
28295
28296  
28297
28298
28299  /*
28300  * - LGPL
28301  *
28302  * menu
28303  * 
28304  */
28305 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28306
28307 /**
28308  * @class Roo.bootstrap.menu.Menu
28309  * @extends Roo.bootstrap.Component
28310  * Bootstrap Menu class - container for Menu
28311  * @cfg {String} html Text of the menu
28312  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28313  * @cfg {String} icon Font awesome icon
28314  * @cfg {String} pos Menu align to (top | bottom) default bottom
28315  * 
28316  * 
28317  * @constructor
28318  * Create a new Menu
28319  * @param {Object} config The config object
28320  */
28321
28322
28323 Roo.bootstrap.menu.Menu = function(config){
28324     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28325     
28326     this.addEvents({
28327         /**
28328          * @event beforeshow
28329          * Fires before this menu is displayed
28330          * @param {Roo.bootstrap.menu.Menu} this
28331          */
28332         beforeshow : true,
28333         /**
28334          * @event beforehide
28335          * Fires before this menu is hidden
28336          * @param {Roo.bootstrap.menu.Menu} this
28337          */
28338         beforehide : true,
28339         /**
28340          * @event show
28341          * Fires after this menu is displayed
28342          * @param {Roo.bootstrap.menu.Menu} this
28343          */
28344         show : true,
28345         /**
28346          * @event hide
28347          * Fires after this menu is hidden
28348          * @param {Roo.bootstrap.menu.Menu} this
28349          */
28350         hide : true,
28351         /**
28352          * @event click
28353          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28354          * @param {Roo.bootstrap.menu.Menu} this
28355          * @param {Roo.EventObject} e
28356          */
28357         click : true
28358     });
28359     
28360 };
28361
28362 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28363     
28364     submenu : false,
28365     html : '',
28366     weight : 'default',
28367     icon : false,
28368     pos : 'bottom',
28369     
28370     
28371     getChildContainer : function() {
28372         if(this.isSubMenu){
28373             return this.el;
28374         }
28375         
28376         return this.el.select('ul.dropdown-menu', true).first();  
28377     },
28378     
28379     getAutoCreate : function()
28380     {
28381         var text = [
28382             {
28383                 tag : 'span',
28384                 cls : 'roo-menu-text',
28385                 html : this.html
28386             }
28387         ];
28388         
28389         if(this.icon){
28390             text.unshift({
28391                 tag : 'i',
28392                 cls : 'fa ' + this.icon
28393             })
28394         }
28395         
28396         
28397         var cfg = {
28398             tag : 'div',
28399             cls : 'btn-group',
28400             cn : [
28401                 {
28402                     tag : 'button',
28403                     cls : 'dropdown-button btn btn-' + this.weight,
28404                     cn : text
28405                 },
28406                 {
28407                     tag : 'button',
28408                     cls : 'dropdown-toggle btn btn-' + this.weight,
28409                     cn : [
28410                         {
28411                             tag : 'span',
28412                             cls : 'caret'
28413                         }
28414                     ]
28415                 },
28416                 {
28417                     tag : 'ul',
28418                     cls : 'dropdown-menu'
28419                 }
28420             ]
28421             
28422         };
28423         
28424         if(this.pos == 'top'){
28425             cfg.cls += ' dropup';
28426         }
28427         
28428         if(this.isSubMenu){
28429             cfg = {
28430                 tag : 'ul',
28431                 cls : 'dropdown-menu'
28432             }
28433         }
28434         
28435         return cfg;
28436     },
28437     
28438     onRender : function(ct, position)
28439     {
28440         this.isSubMenu = ct.hasClass('dropdown-submenu');
28441         
28442         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28443     },
28444     
28445     initEvents : function() 
28446     {
28447         if(this.isSubMenu){
28448             return;
28449         }
28450         
28451         this.hidden = true;
28452         
28453         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28454         this.triggerEl.on('click', this.onTriggerPress, this);
28455         
28456         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28457         this.buttonEl.on('click', this.onClick, this);
28458         
28459     },
28460     
28461     list : function()
28462     {
28463         if(this.isSubMenu){
28464             return this.el;
28465         }
28466         
28467         return this.el.select('ul.dropdown-menu', true).first();
28468     },
28469     
28470     onClick : function(e)
28471     {
28472         this.fireEvent("click", this, e);
28473     },
28474     
28475     onTriggerPress  : function(e)
28476     {   
28477         if (this.isVisible()) {
28478             this.hide();
28479         } else {
28480             this.show();
28481         }
28482     },
28483     
28484     isVisible : function(){
28485         return !this.hidden;
28486     },
28487     
28488     show : function()
28489     {
28490         this.fireEvent("beforeshow", this);
28491         
28492         this.hidden = false;
28493         this.el.addClass('open');
28494         
28495         Roo.get(document).on("mouseup", this.onMouseUp, this);
28496         
28497         this.fireEvent("show", this);
28498         
28499         
28500     },
28501     
28502     hide : function()
28503     {
28504         this.fireEvent("beforehide", this);
28505         
28506         this.hidden = true;
28507         this.el.removeClass('open');
28508         
28509         Roo.get(document).un("mouseup", this.onMouseUp);
28510         
28511         this.fireEvent("hide", this);
28512     },
28513     
28514     onMouseUp : function()
28515     {
28516         this.hide();
28517     }
28518     
28519 });
28520
28521  
28522  /*
28523  * - LGPL
28524  *
28525  * menu item
28526  * 
28527  */
28528 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28529
28530 /**
28531  * @class Roo.bootstrap.menu.Item
28532  * @extends Roo.bootstrap.Component
28533  * Bootstrap MenuItem class
28534  * @cfg {Boolean} submenu (true | false) default false
28535  * @cfg {String} html text of the item
28536  * @cfg {String} href the link
28537  * @cfg {Boolean} disable (true | false) default false
28538  * @cfg {Boolean} preventDefault (true | false) default true
28539  * @cfg {String} icon Font awesome icon
28540  * @cfg {String} pos Submenu align to (left | right) default right 
28541  * 
28542  * 
28543  * @constructor
28544  * Create a new Item
28545  * @param {Object} config The config object
28546  */
28547
28548
28549 Roo.bootstrap.menu.Item = function(config){
28550     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28551     this.addEvents({
28552         /**
28553          * @event mouseover
28554          * Fires when the mouse is hovering over this menu
28555          * @param {Roo.bootstrap.menu.Item} this
28556          * @param {Roo.EventObject} e
28557          */
28558         mouseover : true,
28559         /**
28560          * @event mouseout
28561          * Fires when the mouse exits this menu
28562          * @param {Roo.bootstrap.menu.Item} this
28563          * @param {Roo.EventObject} e
28564          */
28565         mouseout : true,
28566         // raw events
28567         /**
28568          * @event click
28569          * The raw click event for the entire grid.
28570          * @param {Roo.EventObject} e
28571          */
28572         click : true
28573     });
28574 };
28575
28576 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28577     
28578     submenu : false,
28579     href : '',
28580     html : '',
28581     preventDefault: true,
28582     disable : false,
28583     icon : false,
28584     pos : 'right',
28585     
28586     getAutoCreate : function()
28587     {
28588         var text = [
28589             {
28590                 tag : 'span',
28591                 cls : 'roo-menu-item-text',
28592                 html : this.html
28593             }
28594         ];
28595         
28596         if(this.icon){
28597             text.unshift({
28598                 tag : 'i',
28599                 cls : 'fa ' + this.icon
28600             })
28601         }
28602         
28603         var cfg = {
28604             tag : 'li',
28605             cn : [
28606                 {
28607                     tag : 'a',
28608                     href : this.href || '#',
28609                     cn : text
28610                 }
28611             ]
28612         };
28613         
28614         if(this.disable){
28615             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28616         }
28617         
28618         if(this.submenu){
28619             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28620             
28621             if(this.pos == 'left'){
28622                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28623             }
28624         }
28625         
28626         return cfg;
28627     },
28628     
28629     initEvents : function() 
28630     {
28631         this.el.on('mouseover', this.onMouseOver, this);
28632         this.el.on('mouseout', this.onMouseOut, this);
28633         
28634         this.el.select('a', true).first().on('click', this.onClick, this);
28635         
28636     },
28637     
28638     onClick : function(e)
28639     {
28640         if(this.preventDefault){
28641             e.preventDefault();
28642         }
28643         
28644         this.fireEvent("click", this, e);
28645     },
28646     
28647     onMouseOver : function(e)
28648     {
28649         if(this.submenu && this.pos == 'left'){
28650             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28651         }
28652         
28653         this.fireEvent("mouseover", this, e);
28654     },
28655     
28656     onMouseOut : function(e)
28657     {
28658         this.fireEvent("mouseout", this, e);
28659     }
28660 });
28661
28662  
28663
28664  /*
28665  * - LGPL
28666  *
28667  * menu separator
28668  * 
28669  */
28670 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28671
28672 /**
28673  * @class Roo.bootstrap.menu.Separator
28674  * @extends Roo.bootstrap.Component
28675  * Bootstrap Separator class
28676  * 
28677  * @constructor
28678  * Create a new Separator
28679  * @param {Object} config The config object
28680  */
28681
28682
28683 Roo.bootstrap.menu.Separator = function(config){
28684     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28685 };
28686
28687 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28688     
28689     getAutoCreate : function(){
28690         var cfg = {
28691             tag : 'li',
28692             cls: 'divider'
28693         };
28694         
28695         return cfg;
28696     }
28697    
28698 });
28699
28700  
28701
28702  /*
28703  * - LGPL
28704  *
28705  * Tooltip
28706  * 
28707  */
28708
28709 /**
28710  * @class Roo.bootstrap.Tooltip
28711  * Bootstrap Tooltip class
28712  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28713  * to determine which dom element triggers the tooltip.
28714  * 
28715  * It needs to add support for additional attributes like tooltip-position
28716  * 
28717  * @constructor
28718  * Create a new Toolti
28719  * @param {Object} config The config object
28720  */
28721
28722 Roo.bootstrap.Tooltip = function(config){
28723     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28724     
28725     this.alignment = Roo.bootstrap.Tooltip.alignment;
28726     
28727     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28728         this.alignment = config.alignment;
28729     }
28730     
28731 };
28732
28733 Roo.apply(Roo.bootstrap.Tooltip, {
28734     /**
28735      * @function init initialize tooltip monitoring.
28736      * @static
28737      */
28738     currentEl : false,
28739     currentTip : false,
28740     currentRegion : false,
28741     
28742     //  init : delay?
28743     
28744     init : function()
28745     {
28746         Roo.get(document).on('mouseover', this.enter ,this);
28747         Roo.get(document).on('mouseout', this.leave, this);
28748          
28749         
28750         this.currentTip = new Roo.bootstrap.Tooltip();
28751     },
28752     
28753     enter : function(ev)
28754     {
28755         var dom = ev.getTarget();
28756         
28757         //Roo.log(['enter',dom]);
28758         var el = Roo.fly(dom);
28759         if (this.currentEl) {
28760             //Roo.log(dom);
28761             //Roo.log(this.currentEl);
28762             //Roo.log(this.currentEl.contains(dom));
28763             if (this.currentEl == el) {
28764                 return;
28765             }
28766             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28767                 return;
28768             }
28769
28770         }
28771         
28772         if (this.currentTip.el) {
28773             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28774         }    
28775         //Roo.log(ev);
28776         
28777         if(!el || el.dom == document){
28778             return;
28779         }
28780         
28781         var bindEl = el;
28782         
28783         // you can not look for children, as if el is the body.. then everythign is the child..
28784         if (!el.attr('tooltip')) { //
28785             if (!el.select("[tooltip]").elements.length) {
28786                 return;
28787             }
28788             // is the mouse over this child...?
28789             bindEl = el.select("[tooltip]").first();
28790             var xy = ev.getXY();
28791             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28792                 //Roo.log("not in region.");
28793                 return;
28794             }
28795             //Roo.log("child element over..");
28796             
28797         }
28798         this.currentEl = bindEl;
28799         this.currentTip.bind(bindEl);
28800         this.currentRegion = Roo.lib.Region.getRegion(dom);
28801         this.currentTip.enter();
28802         
28803     },
28804     leave : function(ev)
28805     {
28806         var dom = ev.getTarget();
28807         //Roo.log(['leave',dom]);
28808         if (!this.currentEl) {
28809             return;
28810         }
28811         
28812         
28813         if (dom != this.currentEl.dom) {
28814             return;
28815         }
28816         var xy = ev.getXY();
28817         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28818             return;
28819         }
28820         // only activate leave if mouse cursor is outside... bounding box..
28821         
28822         
28823         
28824         
28825         if (this.currentTip) {
28826             this.currentTip.leave();
28827         }
28828         //Roo.log('clear currentEl');
28829         this.currentEl = false;
28830         
28831         
28832     },
28833     alignment : {
28834         'left' : ['r-l', [-2,0], 'right'],
28835         'right' : ['l-r', [2,0], 'left'],
28836         'bottom' : ['t-b', [0,2], 'top'],
28837         'top' : [ 'b-t', [0,-2], 'bottom']
28838     }
28839     
28840 });
28841
28842
28843 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28844     
28845     
28846     bindEl : false,
28847     
28848     delay : null, // can be { show : 300 , hide: 500}
28849     
28850     timeout : null,
28851     
28852     hoverState : null, //???
28853     
28854     placement : 'bottom', 
28855     
28856     alignment : false,
28857     
28858     getAutoCreate : function(){
28859     
28860         var cfg = {
28861            cls : 'tooltip',   
28862            role : 'tooltip',
28863            cn : [
28864                 {
28865                     cls : 'tooltip-arrow arrow'
28866                 },
28867                 {
28868                     cls : 'tooltip-inner'
28869                 }
28870            ]
28871         };
28872         
28873         return cfg;
28874     },
28875     bind : function(el)
28876     {
28877         this.bindEl = el;
28878     },
28879     
28880     initEvents : function()
28881     {
28882         this.arrowEl = this.el.select('.arrow', true).first();
28883         this.innerEl = this.el.select('.tooltip-inner', true).first();
28884     },
28885     
28886     enter : function () {
28887        
28888         if (this.timeout != null) {
28889             clearTimeout(this.timeout);
28890         }
28891         
28892         this.hoverState = 'in';
28893          //Roo.log("enter - show");
28894         if (!this.delay || !this.delay.show) {
28895             this.show();
28896             return;
28897         }
28898         var _t = this;
28899         this.timeout = setTimeout(function () {
28900             if (_t.hoverState == 'in') {
28901                 _t.show();
28902             }
28903         }, this.delay.show);
28904     },
28905     leave : function()
28906     {
28907         clearTimeout(this.timeout);
28908     
28909         this.hoverState = 'out';
28910          if (!this.delay || !this.delay.hide) {
28911             this.hide();
28912             return;
28913         }
28914        
28915         var _t = this;
28916         this.timeout = setTimeout(function () {
28917             //Roo.log("leave - timeout");
28918             
28919             if (_t.hoverState == 'out') {
28920                 _t.hide();
28921                 Roo.bootstrap.Tooltip.currentEl = false;
28922             }
28923         }, delay);
28924     },
28925     
28926     show : function (msg)
28927     {
28928         if (!this.el) {
28929             this.render(document.body);
28930         }
28931         // set content.
28932         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28933         
28934         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28935         
28936         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28937         
28938         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28939                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28940         
28941         var placement = typeof this.placement == 'function' ?
28942             this.placement.call(this, this.el, on_el) :
28943             this.placement;
28944             
28945         var autoToken = /\s?auto?\s?/i;
28946         var autoPlace = autoToken.test(placement);
28947         if (autoPlace) {
28948             placement = placement.replace(autoToken, '') || 'top';
28949         }
28950         
28951         //this.el.detach()
28952         //this.el.setXY([0,0]);
28953         this.el.show();
28954         //this.el.dom.style.display='block';
28955         
28956         //this.el.appendTo(on_el);
28957         
28958         var p = this.getPosition();
28959         var box = this.el.getBox();
28960         
28961         if (autoPlace) {
28962             // fixme..
28963         }
28964         
28965         var align = this.alignment[placement];
28966         
28967         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28968         
28969         if(placement == 'top' || placement == 'bottom'){
28970             if(xy[0] < 0){
28971                 placement = 'right';
28972             }
28973             
28974             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28975                 placement = 'left';
28976             }
28977             
28978             var scroll = Roo.select('body', true).first().getScroll();
28979             
28980             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28981                 placement = 'top';
28982             }
28983             
28984             align = this.alignment[placement];
28985             
28986             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28987             
28988         }
28989         
28990         this.el.alignTo(this.bindEl, align[0],align[1]);
28991         //var arrow = this.el.select('.arrow',true).first();
28992         //arrow.set(align[2], 
28993         
28994         this.el.addClass(placement);
28995         this.el.addClass("bs-tooltip-"+ placement);
28996         
28997         this.el.addClass('in fade show');
28998         
28999         this.hoverState = null;
29000         
29001         if (this.el.hasClass('fade')) {
29002             // fade it?
29003         }
29004         
29005         
29006         
29007         
29008         
29009     },
29010     hide : function()
29011     {
29012          
29013         if (!this.el) {
29014             return;
29015         }
29016         //this.el.setXY([0,0]);
29017         this.el.removeClass(['show', 'in']);
29018         //this.el.hide();
29019         
29020     }
29021     
29022 });
29023  
29024
29025  /*
29026  * - LGPL
29027  *
29028  * Location Picker
29029  * 
29030  */
29031
29032 /**
29033  * @class Roo.bootstrap.LocationPicker
29034  * @extends Roo.bootstrap.Component
29035  * Bootstrap LocationPicker class
29036  * @cfg {Number} latitude Position when init default 0
29037  * @cfg {Number} longitude Position when init default 0
29038  * @cfg {Number} zoom default 15
29039  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29040  * @cfg {Boolean} mapTypeControl default false
29041  * @cfg {Boolean} disableDoubleClickZoom default false
29042  * @cfg {Boolean} scrollwheel default true
29043  * @cfg {Boolean} streetViewControl default false
29044  * @cfg {Number} radius default 0
29045  * @cfg {String} locationName
29046  * @cfg {Boolean} draggable default true
29047  * @cfg {Boolean} enableAutocomplete default false
29048  * @cfg {Boolean} enableReverseGeocode default true
29049  * @cfg {String} markerTitle
29050  * 
29051  * @constructor
29052  * Create a new LocationPicker
29053  * @param {Object} config The config object
29054  */
29055
29056
29057 Roo.bootstrap.LocationPicker = function(config){
29058     
29059     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29060     
29061     this.addEvents({
29062         /**
29063          * @event initial
29064          * Fires when the picker initialized.
29065          * @param {Roo.bootstrap.LocationPicker} this
29066          * @param {Google Location} location
29067          */
29068         initial : true,
29069         /**
29070          * @event positionchanged
29071          * Fires when the picker position changed.
29072          * @param {Roo.bootstrap.LocationPicker} this
29073          * @param {Google Location} location
29074          */
29075         positionchanged : true,
29076         /**
29077          * @event resize
29078          * Fires when the map resize.
29079          * @param {Roo.bootstrap.LocationPicker} this
29080          */
29081         resize : true,
29082         /**
29083          * @event show
29084          * Fires when the map show.
29085          * @param {Roo.bootstrap.LocationPicker} this
29086          */
29087         show : true,
29088         /**
29089          * @event hide
29090          * Fires when the map hide.
29091          * @param {Roo.bootstrap.LocationPicker} this
29092          */
29093         hide : true,
29094         /**
29095          * @event mapClick
29096          * Fires when click the map.
29097          * @param {Roo.bootstrap.LocationPicker} this
29098          * @param {Map event} e
29099          */
29100         mapClick : true,
29101         /**
29102          * @event mapRightClick
29103          * Fires when right click the map.
29104          * @param {Roo.bootstrap.LocationPicker} this
29105          * @param {Map event} e
29106          */
29107         mapRightClick : true,
29108         /**
29109          * @event markerClick
29110          * Fires when click the marker.
29111          * @param {Roo.bootstrap.LocationPicker} this
29112          * @param {Map event} e
29113          */
29114         markerClick : true,
29115         /**
29116          * @event markerRightClick
29117          * Fires when right click the marker.
29118          * @param {Roo.bootstrap.LocationPicker} this
29119          * @param {Map event} e
29120          */
29121         markerRightClick : true,
29122         /**
29123          * @event OverlayViewDraw
29124          * Fires when OverlayView Draw
29125          * @param {Roo.bootstrap.LocationPicker} this
29126          */
29127         OverlayViewDraw : true,
29128         /**
29129          * @event OverlayViewOnAdd
29130          * Fires when OverlayView Draw
29131          * @param {Roo.bootstrap.LocationPicker} this
29132          */
29133         OverlayViewOnAdd : true,
29134         /**
29135          * @event OverlayViewOnRemove
29136          * Fires when OverlayView Draw
29137          * @param {Roo.bootstrap.LocationPicker} this
29138          */
29139         OverlayViewOnRemove : true,
29140         /**
29141          * @event OverlayViewShow
29142          * Fires when OverlayView Draw
29143          * @param {Roo.bootstrap.LocationPicker} this
29144          * @param {Pixel} cpx
29145          */
29146         OverlayViewShow : true,
29147         /**
29148          * @event OverlayViewHide
29149          * Fires when OverlayView Draw
29150          * @param {Roo.bootstrap.LocationPicker} this
29151          */
29152         OverlayViewHide : true,
29153         /**
29154          * @event loadexception
29155          * Fires when load google lib failed.
29156          * @param {Roo.bootstrap.LocationPicker} this
29157          */
29158         loadexception : true
29159     });
29160         
29161 };
29162
29163 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29164     
29165     gMapContext: false,
29166     
29167     latitude: 0,
29168     longitude: 0,
29169     zoom: 15,
29170     mapTypeId: false,
29171     mapTypeControl: false,
29172     disableDoubleClickZoom: false,
29173     scrollwheel: true,
29174     streetViewControl: false,
29175     radius: 0,
29176     locationName: '',
29177     draggable: true,
29178     enableAutocomplete: false,
29179     enableReverseGeocode: true,
29180     markerTitle: '',
29181     
29182     getAutoCreate: function()
29183     {
29184
29185         var cfg = {
29186             tag: 'div',
29187             cls: 'roo-location-picker'
29188         };
29189         
29190         return cfg
29191     },
29192     
29193     initEvents: function(ct, position)
29194     {       
29195         if(!this.el.getWidth() || this.isApplied()){
29196             return;
29197         }
29198         
29199         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29200         
29201         this.initial();
29202     },
29203     
29204     initial: function()
29205     {
29206         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29207             this.fireEvent('loadexception', this);
29208             return;
29209         }
29210         
29211         if(!this.mapTypeId){
29212             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29213         }
29214         
29215         this.gMapContext = this.GMapContext();
29216         
29217         this.initOverlayView();
29218         
29219         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29220         
29221         var _this = this;
29222                 
29223         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29224             _this.setPosition(_this.gMapContext.marker.position);
29225         });
29226         
29227         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29228             _this.fireEvent('mapClick', this, event);
29229             
29230         });
29231
29232         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29233             _this.fireEvent('mapRightClick', this, event);
29234             
29235         });
29236         
29237         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29238             _this.fireEvent('markerClick', this, event);
29239             
29240         });
29241
29242         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29243             _this.fireEvent('markerRightClick', this, event);
29244             
29245         });
29246         
29247         this.setPosition(this.gMapContext.location);
29248         
29249         this.fireEvent('initial', this, this.gMapContext.location);
29250     },
29251     
29252     initOverlayView: function()
29253     {
29254         var _this = this;
29255         
29256         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29257             
29258             draw: function()
29259             {
29260                 _this.fireEvent('OverlayViewDraw', _this);
29261             },
29262             
29263             onAdd: function()
29264             {
29265                 _this.fireEvent('OverlayViewOnAdd', _this);
29266             },
29267             
29268             onRemove: function()
29269             {
29270                 _this.fireEvent('OverlayViewOnRemove', _this);
29271             },
29272             
29273             show: function(cpx)
29274             {
29275                 _this.fireEvent('OverlayViewShow', _this, cpx);
29276             },
29277             
29278             hide: function()
29279             {
29280                 _this.fireEvent('OverlayViewHide', _this);
29281             }
29282             
29283         });
29284     },
29285     
29286     fromLatLngToContainerPixel: function(event)
29287     {
29288         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29289     },
29290     
29291     isApplied: function() 
29292     {
29293         return this.getGmapContext() == false ? false : true;
29294     },
29295     
29296     getGmapContext: function() 
29297     {
29298         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29299     },
29300     
29301     GMapContext: function() 
29302     {
29303         var position = new google.maps.LatLng(this.latitude, this.longitude);
29304         
29305         var _map = new google.maps.Map(this.el.dom, {
29306             center: position,
29307             zoom: this.zoom,
29308             mapTypeId: this.mapTypeId,
29309             mapTypeControl: this.mapTypeControl,
29310             disableDoubleClickZoom: this.disableDoubleClickZoom,
29311             scrollwheel: this.scrollwheel,
29312             streetViewControl: this.streetViewControl,
29313             locationName: this.locationName,
29314             draggable: this.draggable,
29315             enableAutocomplete: this.enableAutocomplete,
29316             enableReverseGeocode: this.enableReverseGeocode
29317         });
29318         
29319         var _marker = new google.maps.Marker({
29320             position: position,
29321             map: _map,
29322             title: this.markerTitle,
29323             draggable: this.draggable
29324         });
29325         
29326         return {
29327             map: _map,
29328             marker: _marker,
29329             circle: null,
29330             location: position,
29331             radius: this.radius,
29332             locationName: this.locationName,
29333             addressComponents: {
29334                 formatted_address: null,
29335                 addressLine1: null,
29336                 addressLine2: null,
29337                 streetName: null,
29338                 streetNumber: null,
29339                 city: null,
29340                 district: null,
29341                 state: null,
29342                 stateOrProvince: null
29343             },
29344             settings: this,
29345             domContainer: this.el.dom,
29346             geodecoder: new google.maps.Geocoder()
29347         };
29348     },
29349     
29350     drawCircle: function(center, radius, options) 
29351     {
29352         if (this.gMapContext.circle != null) {
29353             this.gMapContext.circle.setMap(null);
29354         }
29355         if (radius > 0) {
29356             radius *= 1;
29357             options = Roo.apply({}, options, {
29358                 strokeColor: "#0000FF",
29359                 strokeOpacity: .35,
29360                 strokeWeight: 2,
29361                 fillColor: "#0000FF",
29362                 fillOpacity: .2
29363             });
29364             
29365             options.map = this.gMapContext.map;
29366             options.radius = radius;
29367             options.center = center;
29368             this.gMapContext.circle = new google.maps.Circle(options);
29369             return this.gMapContext.circle;
29370         }
29371         
29372         return null;
29373     },
29374     
29375     setPosition: function(location) 
29376     {
29377         this.gMapContext.location = location;
29378         this.gMapContext.marker.setPosition(location);
29379         this.gMapContext.map.panTo(location);
29380         this.drawCircle(location, this.gMapContext.radius, {});
29381         
29382         var _this = this;
29383         
29384         if (this.gMapContext.settings.enableReverseGeocode) {
29385             this.gMapContext.geodecoder.geocode({
29386                 latLng: this.gMapContext.location
29387             }, function(results, status) {
29388                 
29389                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29390                     _this.gMapContext.locationName = results[0].formatted_address;
29391                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29392                     
29393                     _this.fireEvent('positionchanged', this, location);
29394                 }
29395             });
29396             
29397             return;
29398         }
29399         
29400         this.fireEvent('positionchanged', this, location);
29401     },
29402     
29403     resize: function()
29404     {
29405         google.maps.event.trigger(this.gMapContext.map, "resize");
29406         
29407         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29408         
29409         this.fireEvent('resize', this);
29410     },
29411     
29412     setPositionByLatLng: function(latitude, longitude)
29413     {
29414         this.setPosition(new google.maps.LatLng(latitude, longitude));
29415     },
29416     
29417     getCurrentPosition: function() 
29418     {
29419         return {
29420             latitude: this.gMapContext.location.lat(),
29421             longitude: this.gMapContext.location.lng()
29422         };
29423     },
29424     
29425     getAddressName: function() 
29426     {
29427         return this.gMapContext.locationName;
29428     },
29429     
29430     getAddressComponents: function() 
29431     {
29432         return this.gMapContext.addressComponents;
29433     },
29434     
29435     address_component_from_google_geocode: function(address_components) 
29436     {
29437         var result = {};
29438         
29439         for (var i = 0; i < address_components.length; i++) {
29440             var component = address_components[i];
29441             if (component.types.indexOf("postal_code") >= 0) {
29442                 result.postalCode = component.short_name;
29443             } else if (component.types.indexOf("street_number") >= 0) {
29444                 result.streetNumber = component.short_name;
29445             } else if (component.types.indexOf("route") >= 0) {
29446                 result.streetName = component.short_name;
29447             } else if (component.types.indexOf("neighborhood") >= 0) {
29448                 result.city = component.short_name;
29449             } else if (component.types.indexOf("locality") >= 0) {
29450                 result.city = component.short_name;
29451             } else if (component.types.indexOf("sublocality") >= 0) {
29452                 result.district = component.short_name;
29453             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29454                 result.stateOrProvince = component.short_name;
29455             } else if (component.types.indexOf("country") >= 0) {
29456                 result.country = component.short_name;
29457             }
29458         }
29459         
29460         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29461         result.addressLine2 = "";
29462         return result;
29463     },
29464     
29465     setZoomLevel: function(zoom)
29466     {
29467         this.gMapContext.map.setZoom(zoom);
29468     },
29469     
29470     show: function()
29471     {
29472         if(!this.el){
29473             return;
29474         }
29475         
29476         this.el.show();
29477         
29478         this.resize();
29479         
29480         this.fireEvent('show', this);
29481     },
29482     
29483     hide: function()
29484     {
29485         if(!this.el){
29486             return;
29487         }
29488         
29489         this.el.hide();
29490         
29491         this.fireEvent('hide', this);
29492     }
29493     
29494 });
29495
29496 Roo.apply(Roo.bootstrap.LocationPicker, {
29497     
29498     OverlayView : function(map, options)
29499     {
29500         options = options || {};
29501         
29502         this.setMap(map);
29503     }
29504     
29505     
29506 });/**
29507  * @class Roo.bootstrap.Alert
29508  * @extends Roo.bootstrap.Component
29509  * Bootstrap Alert class - shows an alert area box
29510  * eg
29511  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29512   Enter a valid email address
29513 </div>
29514  * @licence LGPL
29515  * @cfg {String} title The title of alert
29516  * @cfg {String} html The content of alert
29517  * @cfg {String} weight (  success | info | warning | danger )
29518  * @cfg {String} faicon font-awesomeicon
29519  * 
29520  * @constructor
29521  * Create a new alert
29522  * @param {Object} config The config object
29523  */
29524
29525
29526 Roo.bootstrap.Alert = function(config){
29527     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29528     
29529 };
29530
29531 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29532     
29533     title: '',
29534     html: '',
29535     weight: false,
29536     faicon: false,
29537     
29538     getAutoCreate : function()
29539     {
29540         
29541         var cfg = {
29542             tag : 'div',
29543             cls : 'alert',
29544             cn : [
29545                 {
29546                     tag : 'i',
29547                     cls : 'roo-alert-icon'
29548                     
29549                 },
29550                 {
29551                     tag : 'b',
29552                     cls : 'roo-alert-title',
29553                     html : this.title
29554                 },
29555                 {
29556                     tag : 'span',
29557                     cls : 'roo-alert-text',
29558                     html : this.html
29559                 }
29560             ]
29561         };
29562         
29563         if(this.faicon){
29564             cfg.cn[0].cls += ' fa ' + this.faicon;
29565         }
29566         
29567         if(this.weight){
29568             cfg.cls += ' alert-' + this.weight;
29569         }
29570         
29571         return cfg;
29572     },
29573     
29574     initEvents: function() 
29575     {
29576         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29577     },
29578     
29579     setTitle : function(str)
29580     {
29581         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29582     },
29583     
29584     setText : function(str)
29585     {
29586         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29587     },
29588     
29589     setWeight : function(weight)
29590     {
29591         if(this.weight){
29592             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29593         }
29594         
29595         this.weight = weight;
29596         
29597         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29598     },
29599     
29600     setIcon : function(icon)
29601     {
29602         if(this.faicon){
29603             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29604         }
29605         
29606         this.faicon = icon;
29607         
29608         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29609     },
29610     
29611     hide: function() 
29612     {
29613         this.el.hide();   
29614     },
29615     
29616     show: function() 
29617     {  
29618         this.el.show();   
29619     }
29620     
29621 });
29622
29623  
29624 /*
29625 * Licence: LGPL
29626 */
29627
29628 /**
29629  * @class Roo.bootstrap.UploadCropbox
29630  * @extends Roo.bootstrap.Component
29631  * Bootstrap UploadCropbox class
29632  * @cfg {String} emptyText show when image has been loaded
29633  * @cfg {String} rotateNotify show when image too small to rotate
29634  * @cfg {Number} errorTimeout default 3000
29635  * @cfg {Number} minWidth default 300
29636  * @cfg {Number} minHeight default 300
29637  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29638  * @cfg {Boolean} isDocument (true|false) default false
29639  * @cfg {String} url action url
29640  * @cfg {String} paramName default 'imageUpload'
29641  * @cfg {String} method default POST
29642  * @cfg {Boolean} loadMask (true|false) default true
29643  * @cfg {Boolean} loadingText default 'Loading...'
29644  * 
29645  * @constructor
29646  * Create a new UploadCropbox
29647  * @param {Object} config The config object
29648  */
29649
29650 Roo.bootstrap.UploadCropbox = function(config){
29651     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29652     
29653     this.addEvents({
29654         /**
29655          * @event beforeselectfile
29656          * Fire before select file
29657          * @param {Roo.bootstrap.UploadCropbox} this
29658          */
29659         "beforeselectfile" : true,
29660         /**
29661          * @event initial
29662          * Fire after initEvent
29663          * @param {Roo.bootstrap.UploadCropbox} this
29664          */
29665         "initial" : true,
29666         /**
29667          * @event crop
29668          * Fire after initEvent
29669          * @param {Roo.bootstrap.UploadCropbox} this
29670          * @param {String} data
29671          */
29672         "crop" : true,
29673         /**
29674          * @event prepare
29675          * Fire when preparing the file data
29676          * @param {Roo.bootstrap.UploadCropbox} this
29677          * @param {Object} file
29678          */
29679         "prepare" : true,
29680         /**
29681          * @event exception
29682          * Fire when get exception
29683          * @param {Roo.bootstrap.UploadCropbox} this
29684          * @param {XMLHttpRequest} xhr
29685          */
29686         "exception" : true,
29687         /**
29688          * @event beforeloadcanvas
29689          * Fire before load the canvas
29690          * @param {Roo.bootstrap.UploadCropbox} this
29691          * @param {String} src
29692          */
29693         "beforeloadcanvas" : true,
29694         /**
29695          * @event trash
29696          * Fire when trash image
29697          * @param {Roo.bootstrap.UploadCropbox} this
29698          */
29699         "trash" : true,
29700         /**
29701          * @event download
29702          * Fire when download the image
29703          * @param {Roo.bootstrap.UploadCropbox} this
29704          */
29705         "download" : true,
29706         /**
29707          * @event footerbuttonclick
29708          * Fire when footerbuttonclick
29709          * @param {Roo.bootstrap.UploadCropbox} this
29710          * @param {String} type
29711          */
29712         "footerbuttonclick" : true,
29713         /**
29714          * @event resize
29715          * Fire when resize
29716          * @param {Roo.bootstrap.UploadCropbox} this
29717          */
29718         "resize" : true,
29719         /**
29720          * @event rotate
29721          * Fire when rotate the image
29722          * @param {Roo.bootstrap.UploadCropbox} this
29723          * @param {String} pos
29724          */
29725         "rotate" : true,
29726         /**
29727          * @event inspect
29728          * Fire when inspect the file
29729          * @param {Roo.bootstrap.UploadCropbox} this
29730          * @param {Object} file
29731          */
29732         "inspect" : true,
29733         /**
29734          * @event upload
29735          * Fire when xhr upload the file
29736          * @param {Roo.bootstrap.UploadCropbox} this
29737          * @param {Object} data
29738          */
29739         "upload" : true,
29740         /**
29741          * @event arrange
29742          * Fire when arrange the file data
29743          * @param {Roo.bootstrap.UploadCropbox} this
29744          * @param {Object} formData
29745          */
29746         "arrange" : true
29747     });
29748     
29749     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29750 };
29751
29752 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29753     
29754     emptyText : 'Click to upload image',
29755     rotateNotify : 'Image is too small to rotate',
29756     errorTimeout : 3000,
29757     scale : 0,
29758     baseScale : 1,
29759     rotate : 0,
29760     dragable : false,
29761     pinching : false,
29762     mouseX : 0,
29763     mouseY : 0,
29764     cropData : false,
29765     minWidth : 300,
29766     minHeight : 300,
29767     file : false,
29768     exif : {},
29769     baseRotate : 1,
29770     cropType : 'image/jpeg',
29771     buttons : false,
29772     canvasLoaded : false,
29773     isDocument : false,
29774     method : 'POST',
29775     paramName : 'imageUpload',
29776     loadMask : true,
29777     loadingText : 'Loading...',
29778     maskEl : false,
29779     
29780     getAutoCreate : function()
29781     {
29782         var cfg = {
29783             tag : 'div',
29784             cls : 'roo-upload-cropbox',
29785             cn : [
29786                 {
29787                     tag : 'input',
29788                     cls : 'roo-upload-cropbox-selector',
29789                     type : 'file'
29790                 },
29791                 {
29792                     tag : 'div',
29793                     cls : 'roo-upload-cropbox-body',
29794                     style : 'cursor:pointer',
29795                     cn : [
29796                         {
29797                             tag : 'div',
29798                             cls : 'roo-upload-cropbox-preview'
29799                         },
29800                         {
29801                             tag : 'div',
29802                             cls : 'roo-upload-cropbox-thumb'
29803                         },
29804                         {
29805                             tag : 'div',
29806                             cls : 'roo-upload-cropbox-empty-notify',
29807                             html : this.emptyText
29808                         },
29809                         {
29810                             tag : 'div',
29811                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29812                             html : this.rotateNotify
29813                         }
29814                     ]
29815                 },
29816                 {
29817                     tag : 'div',
29818                     cls : 'roo-upload-cropbox-footer',
29819                     cn : {
29820                         tag : 'div',
29821                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29822                         cn : []
29823                     }
29824                 }
29825             ]
29826         };
29827         
29828         return cfg;
29829     },
29830     
29831     onRender : function(ct, position)
29832     {
29833         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29834         
29835         if (this.buttons.length) {
29836             
29837             Roo.each(this.buttons, function(bb) {
29838                 
29839                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29840                 
29841                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29842                 
29843             }, this);
29844         }
29845         
29846         if(this.loadMask){
29847             this.maskEl = this.el;
29848         }
29849     },
29850     
29851     initEvents : function()
29852     {
29853         this.urlAPI = (window.createObjectURL && window) || 
29854                                 (window.URL && URL.revokeObjectURL && URL) || 
29855                                 (window.webkitURL && webkitURL);
29856                         
29857         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29858         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29859         
29860         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29861         this.selectorEl.hide();
29862         
29863         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29864         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29865         
29866         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29867         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29868         this.thumbEl.hide();
29869         
29870         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29871         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29872         
29873         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29874         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29875         this.errorEl.hide();
29876         
29877         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29878         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29879         this.footerEl.hide();
29880         
29881         this.setThumbBoxSize();
29882         
29883         this.bind();
29884         
29885         this.resize();
29886         
29887         this.fireEvent('initial', this);
29888     },
29889
29890     bind : function()
29891     {
29892         var _this = this;
29893         
29894         window.addEventListener("resize", function() { _this.resize(); } );
29895         
29896         this.bodyEl.on('click', this.beforeSelectFile, this);
29897         
29898         if(Roo.isTouch){
29899             this.bodyEl.on('touchstart', this.onTouchStart, this);
29900             this.bodyEl.on('touchmove', this.onTouchMove, this);
29901             this.bodyEl.on('touchend', this.onTouchEnd, this);
29902         }
29903         
29904         if(!Roo.isTouch){
29905             this.bodyEl.on('mousedown', this.onMouseDown, this);
29906             this.bodyEl.on('mousemove', this.onMouseMove, this);
29907             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29908             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29909             Roo.get(document).on('mouseup', this.onMouseUp, this);
29910         }
29911         
29912         this.selectorEl.on('change', this.onFileSelected, this);
29913     },
29914     
29915     reset : function()
29916     {    
29917         this.scale = 0;
29918         this.baseScale = 1;
29919         this.rotate = 0;
29920         this.baseRotate = 1;
29921         this.dragable = false;
29922         this.pinching = false;
29923         this.mouseX = 0;
29924         this.mouseY = 0;
29925         this.cropData = false;
29926         this.notifyEl.dom.innerHTML = this.emptyText;
29927         
29928         this.selectorEl.dom.value = '';
29929         
29930     },
29931     
29932     resize : function()
29933     {
29934         if(this.fireEvent('resize', this) != false){
29935             this.setThumbBoxPosition();
29936             this.setCanvasPosition();
29937         }
29938     },
29939     
29940     onFooterButtonClick : function(e, el, o, type)
29941     {
29942         switch (type) {
29943             case 'rotate-left' :
29944                 this.onRotateLeft(e);
29945                 break;
29946             case 'rotate-right' :
29947                 this.onRotateRight(e);
29948                 break;
29949             case 'picture' :
29950                 this.beforeSelectFile(e);
29951                 break;
29952             case 'trash' :
29953                 this.trash(e);
29954                 break;
29955             case 'crop' :
29956                 this.crop(e);
29957                 break;
29958             case 'download' :
29959                 this.download(e);
29960                 break;
29961             default :
29962                 break;
29963         }
29964         
29965         this.fireEvent('footerbuttonclick', this, type);
29966     },
29967     
29968     beforeSelectFile : function(e)
29969     {
29970         e.preventDefault();
29971         
29972         if(this.fireEvent('beforeselectfile', this) != false){
29973             this.selectorEl.dom.click();
29974         }
29975     },
29976     
29977     onFileSelected : function(e)
29978     {
29979         e.preventDefault();
29980         
29981         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29982             return;
29983         }
29984         
29985         var file = this.selectorEl.dom.files[0];
29986         
29987         if(this.fireEvent('inspect', this, file) != false){
29988             this.prepare(file);
29989         }
29990         
29991     },
29992     
29993     trash : function(e)
29994     {
29995         this.fireEvent('trash', this);
29996     },
29997     
29998     download : function(e)
29999     {
30000         this.fireEvent('download', this);
30001     },
30002     
30003     loadCanvas : function(src)
30004     {   
30005         if(this.fireEvent('beforeloadcanvas', this, src) != false){
30006             
30007             this.reset();
30008             
30009             this.imageEl = document.createElement('img');
30010             
30011             var _this = this;
30012             
30013             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
30014             
30015             this.imageEl.src = src;
30016         }
30017     },
30018     
30019     onLoadCanvas : function()
30020     {   
30021         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30022         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30023         
30024         this.bodyEl.un('click', this.beforeSelectFile, this);
30025         
30026         this.notifyEl.hide();
30027         this.thumbEl.show();
30028         this.footerEl.show();
30029         
30030         this.baseRotateLevel();
30031         
30032         if(this.isDocument){
30033             this.setThumbBoxSize();
30034         }
30035         
30036         this.setThumbBoxPosition();
30037         
30038         this.baseScaleLevel();
30039         
30040         this.draw();
30041         
30042         this.resize();
30043         
30044         this.canvasLoaded = true;
30045         
30046         if(this.loadMask){
30047             this.maskEl.unmask();
30048         }
30049         
30050     },
30051     
30052     setCanvasPosition : function()
30053     {   
30054         if(!this.canvasEl){
30055             return;
30056         }
30057         
30058         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30059         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30060         
30061         this.previewEl.setLeft(pw);
30062         this.previewEl.setTop(ph);
30063         
30064     },
30065     
30066     onMouseDown : function(e)
30067     {   
30068         e.stopEvent();
30069         
30070         this.dragable = true;
30071         this.pinching = false;
30072         
30073         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30074             this.dragable = false;
30075             return;
30076         }
30077         
30078         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30079         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30080         
30081     },
30082     
30083     onMouseMove : function(e)
30084     {   
30085         e.stopEvent();
30086         
30087         if(!this.canvasLoaded){
30088             return;
30089         }
30090         
30091         if (!this.dragable){
30092             return;
30093         }
30094         
30095         var minX = Math.ceil(this.thumbEl.getLeft(true));
30096         var minY = Math.ceil(this.thumbEl.getTop(true));
30097         
30098         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30099         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30100         
30101         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30102         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30103         
30104         x = x - this.mouseX;
30105         y = y - this.mouseY;
30106         
30107         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30108         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30109         
30110         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30111         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30112         
30113         this.previewEl.setLeft(bgX);
30114         this.previewEl.setTop(bgY);
30115         
30116         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30117         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30118     },
30119     
30120     onMouseUp : function(e)
30121     {   
30122         e.stopEvent();
30123         
30124         this.dragable = false;
30125     },
30126     
30127     onMouseWheel : function(e)
30128     {   
30129         e.stopEvent();
30130         
30131         this.startScale = this.scale;
30132         
30133         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30134         
30135         if(!this.zoomable()){
30136             this.scale = this.startScale;
30137             return;
30138         }
30139         
30140         this.draw();
30141         
30142         return;
30143     },
30144     
30145     zoomable : function()
30146     {
30147         var minScale = this.thumbEl.getWidth() / this.minWidth;
30148         
30149         if(this.minWidth < this.minHeight){
30150             minScale = this.thumbEl.getHeight() / this.minHeight;
30151         }
30152         
30153         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30154         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30155         
30156         if(
30157                 this.isDocument &&
30158                 (this.rotate == 0 || this.rotate == 180) && 
30159                 (
30160                     width > this.imageEl.OriginWidth || 
30161                     height > this.imageEl.OriginHeight ||
30162                     (width < this.minWidth && height < this.minHeight)
30163                 )
30164         ){
30165             return false;
30166         }
30167         
30168         if(
30169                 this.isDocument &&
30170                 (this.rotate == 90 || this.rotate == 270) && 
30171                 (
30172                     width > this.imageEl.OriginWidth || 
30173                     height > this.imageEl.OriginHeight ||
30174                     (width < this.minHeight && height < this.minWidth)
30175                 )
30176         ){
30177             return false;
30178         }
30179         
30180         if(
30181                 !this.isDocument &&
30182                 (this.rotate == 0 || this.rotate == 180) && 
30183                 (
30184                     width < this.minWidth || 
30185                     width > this.imageEl.OriginWidth || 
30186                     height < this.minHeight || 
30187                     height > this.imageEl.OriginHeight
30188                 )
30189         ){
30190             return false;
30191         }
30192         
30193         if(
30194                 !this.isDocument &&
30195                 (this.rotate == 90 || this.rotate == 270) && 
30196                 (
30197                     width < this.minHeight || 
30198                     width > this.imageEl.OriginWidth || 
30199                     height < this.minWidth || 
30200                     height > this.imageEl.OriginHeight
30201                 )
30202         ){
30203             return false;
30204         }
30205         
30206         return true;
30207         
30208     },
30209     
30210     onRotateLeft : function(e)
30211     {   
30212         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30213             
30214             var minScale = this.thumbEl.getWidth() / this.minWidth;
30215             
30216             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30217             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30218             
30219             this.startScale = this.scale;
30220             
30221             while (this.getScaleLevel() < minScale){
30222             
30223                 this.scale = this.scale + 1;
30224                 
30225                 if(!this.zoomable()){
30226                     break;
30227                 }
30228                 
30229                 if(
30230                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30231                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30232                 ){
30233                     continue;
30234                 }
30235                 
30236                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30237
30238                 this.draw();
30239                 
30240                 return;
30241             }
30242             
30243             this.scale = this.startScale;
30244             
30245             this.onRotateFail();
30246             
30247             return false;
30248         }
30249         
30250         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30251
30252         if(this.isDocument){
30253             this.setThumbBoxSize();
30254             this.setThumbBoxPosition();
30255             this.setCanvasPosition();
30256         }
30257         
30258         this.draw();
30259         
30260         this.fireEvent('rotate', this, 'left');
30261         
30262     },
30263     
30264     onRotateRight : function(e)
30265     {
30266         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30267             
30268             var minScale = this.thumbEl.getWidth() / this.minWidth;
30269         
30270             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30271             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30272             
30273             this.startScale = this.scale;
30274             
30275             while (this.getScaleLevel() < minScale){
30276             
30277                 this.scale = this.scale + 1;
30278                 
30279                 if(!this.zoomable()){
30280                     break;
30281                 }
30282                 
30283                 if(
30284                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30285                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30286                 ){
30287                     continue;
30288                 }
30289                 
30290                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30291
30292                 this.draw();
30293                 
30294                 return;
30295             }
30296             
30297             this.scale = this.startScale;
30298             
30299             this.onRotateFail();
30300             
30301             return false;
30302         }
30303         
30304         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30305
30306         if(this.isDocument){
30307             this.setThumbBoxSize();
30308             this.setThumbBoxPosition();
30309             this.setCanvasPosition();
30310         }
30311         
30312         this.draw();
30313         
30314         this.fireEvent('rotate', this, 'right');
30315     },
30316     
30317     onRotateFail : function()
30318     {
30319         this.errorEl.show(true);
30320         
30321         var _this = this;
30322         
30323         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30324     },
30325     
30326     draw : function()
30327     {
30328         this.previewEl.dom.innerHTML = '';
30329         
30330         var canvasEl = document.createElement("canvas");
30331         
30332         var contextEl = canvasEl.getContext("2d");
30333         
30334         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30335         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30336         var center = this.imageEl.OriginWidth / 2;
30337         
30338         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30339             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30340             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30341             center = this.imageEl.OriginHeight / 2;
30342         }
30343         
30344         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30345         
30346         contextEl.translate(center, center);
30347         contextEl.rotate(this.rotate * Math.PI / 180);
30348
30349         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30350         
30351         this.canvasEl = document.createElement("canvas");
30352         
30353         this.contextEl = this.canvasEl.getContext("2d");
30354         
30355         switch (this.rotate) {
30356             case 0 :
30357                 
30358                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30359                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30360                 
30361                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30362                 
30363                 break;
30364             case 90 : 
30365                 
30366                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30367                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30368                 
30369                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30370                     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);
30371                     break;
30372                 }
30373                 
30374                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30375                 
30376                 break;
30377             case 180 :
30378                 
30379                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30380                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30381                 
30382                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30383                     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);
30384                     break;
30385                 }
30386                 
30387                 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);
30388                 
30389                 break;
30390             case 270 :
30391                 
30392                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30393                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30394         
30395                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30396                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30397                     break;
30398                 }
30399                 
30400                 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);
30401                 
30402                 break;
30403             default : 
30404                 break;
30405         }
30406         
30407         this.previewEl.appendChild(this.canvasEl);
30408         
30409         this.setCanvasPosition();
30410     },
30411     
30412     crop : function()
30413     {
30414         if(!this.canvasLoaded){
30415             return;
30416         }
30417         
30418         var imageCanvas = document.createElement("canvas");
30419         
30420         var imageContext = imageCanvas.getContext("2d");
30421         
30422         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30423         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30424         
30425         var center = imageCanvas.width / 2;
30426         
30427         imageContext.translate(center, center);
30428         
30429         imageContext.rotate(this.rotate * Math.PI / 180);
30430         
30431         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30432         
30433         var canvas = document.createElement("canvas");
30434         
30435         var context = canvas.getContext("2d");
30436                 
30437         canvas.width = this.minWidth;
30438         canvas.height = this.minHeight;
30439
30440         switch (this.rotate) {
30441             case 0 :
30442                 
30443                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30444                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30445                 
30446                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30447                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30448                 
30449                 var targetWidth = this.minWidth - 2 * x;
30450                 var targetHeight = this.minHeight - 2 * y;
30451                 
30452                 var scale = 1;
30453                 
30454                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30455                     scale = targetWidth / width;
30456                 }
30457                 
30458                 if(x > 0 && y == 0){
30459                     scale = targetHeight / height;
30460                 }
30461                 
30462                 if(x > 0 && y > 0){
30463                     scale = targetWidth / width;
30464                     
30465                     if(width < height){
30466                         scale = targetHeight / height;
30467                     }
30468                 }
30469                 
30470                 context.scale(scale, scale);
30471                 
30472                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30473                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30474
30475                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30476                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30477
30478                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30479                 
30480                 break;
30481             case 90 : 
30482                 
30483                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30484                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30485                 
30486                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30487                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30488                 
30489                 var targetWidth = this.minWidth - 2 * x;
30490                 var targetHeight = this.minHeight - 2 * y;
30491                 
30492                 var scale = 1;
30493                 
30494                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30495                     scale = targetWidth / width;
30496                 }
30497                 
30498                 if(x > 0 && y == 0){
30499                     scale = targetHeight / height;
30500                 }
30501                 
30502                 if(x > 0 && y > 0){
30503                     scale = targetWidth / width;
30504                     
30505                     if(width < height){
30506                         scale = targetHeight / height;
30507                     }
30508                 }
30509                 
30510                 context.scale(scale, scale);
30511                 
30512                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30513                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30514
30515                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30516                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30517                 
30518                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30519                 
30520                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30521                 
30522                 break;
30523             case 180 :
30524                 
30525                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30526                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30527                 
30528                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30529                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30530                 
30531                 var targetWidth = this.minWidth - 2 * x;
30532                 var targetHeight = this.minHeight - 2 * y;
30533                 
30534                 var scale = 1;
30535                 
30536                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30537                     scale = targetWidth / width;
30538                 }
30539                 
30540                 if(x > 0 && y == 0){
30541                     scale = targetHeight / height;
30542                 }
30543                 
30544                 if(x > 0 && y > 0){
30545                     scale = targetWidth / width;
30546                     
30547                     if(width < height){
30548                         scale = targetHeight / height;
30549                     }
30550                 }
30551                 
30552                 context.scale(scale, scale);
30553                 
30554                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30555                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30556
30557                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30558                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30559
30560                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30561                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30562                 
30563                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30564                 
30565                 break;
30566             case 270 :
30567                 
30568                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30569                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30570                 
30571                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30572                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30573                 
30574                 var targetWidth = this.minWidth - 2 * x;
30575                 var targetHeight = this.minHeight - 2 * y;
30576                 
30577                 var scale = 1;
30578                 
30579                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30580                     scale = targetWidth / width;
30581                 }
30582                 
30583                 if(x > 0 && y == 0){
30584                     scale = targetHeight / height;
30585                 }
30586                 
30587                 if(x > 0 && y > 0){
30588                     scale = targetWidth / width;
30589                     
30590                     if(width < height){
30591                         scale = targetHeight / height;
30592                     }
30593                 }
30594                 
30595                 context.scale(scale, scale);
30596                 
30597                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30598                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30599
30600                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30601                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30602                 
30603                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30604                 
30605                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30606                 
30607                 break;
30608             default : 
30609                 break;
30610         }
30611         
30612         this.cropData = canvas.toDataURL(this.cropType);
30613         
30614         if(this.fireEvent('crop', this, this.cropData) !== false){
30615             this.process(this.file, this.cropData);
30616         }
30617         
30618         return;
30619         
30620     },
30621     
30622     setThumbBoxSize : function()
30623     {
30624         var width, height;
30625         
30626         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30627             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30628             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30629             
30630             this.minWidth = width;
30631             this.minHeight = height;
30632             
30633             if(this.rotate == 90 || this.rotate == 270){
30634                 this.minWidth = height;
30635                 this.minHeight = width;
30636             }
30637         }
30638         
30639         height = 300;
30640         width = Math.ceil(this.minWidth * height / this.minHeight);
30641         
30642         if(this.minWidth > this.minHeight){
30643             width = 300;
30644             height = Math.ceil(this.minHeight * width / this.minWidth);
30645         }
30646         
30647         this.thumbEl.setStyle({
30648             width : width + 'px',
30649             height : height + 'px'
30650         });
30651
30652         return;
30653             
30654     },
30655     
30656     setThumbBoxPosition : function()
30657     {
30658         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30659         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30660         
30661         this.thumbEl.setLeft(x);
30662         this.thumbEl.setTop(y);
30663         
30664     },
30665     
30666     baseRotateLevel : function()
30667     {
30668         this.baseRotate = 1;
30669         
30670         if(
30671                 typeof(this.exif) != 'undefined' &&
30672                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30673                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30674         ){
30675             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30676         }
30677         
30678         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30679         
30680     },
30681     
30682     baseScaleLevel : function()
30683     {
30684         var width, height;
30685         
30686         if(this.isDocument){
30687             
30688             if(this.baseRotate == 6 || this.baseRotate == 8){
30689             
30690                 height = this.thumbEl.getHeight();
30691                 this.baseScale = height / this.imageEl.OriginWidth;
30692
30693                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30694                     width = this.thumbEl.getWidth();
30695                     this.baseScale = width / this.imageEl.OriginHeight;
30696                 }
30697
30698                 return;
30699             }
30700
30701             height = this.thumbEl.getHeight();
30702             this.baseScale = height / this.imageEl.OriginHeight;
30703
30704             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30705                 width = this.thumbEl.getWidth();
30706                 this.baseScale = width / this.imageEl.OriginWidth;
30707             }
30708
30709             return;
30710         }
30711         
30712         if(this.baseRotate == 6 || this.baseRotate == 8){
30713             
30714             width = this.thumbEl.getHeight();
30715             this.baseScale = width / this.imageEl.OriginHeight;
30716             
30717             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30718                 height = this.thumbEl.getWidth();
30719                 this.baseScale = height / this.imageEl.OriginHeight;
30720             }
30721             
30722             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30723                 height = this.thumbEl.getWidth();
30724                 this.baseScale = height / this.imageEl.OriginHeight;
30725                 
30726                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30727                     width = this.thumbEl.getHeight();
30728                     this.baseScale = width / this.imageEl.OriginWidth;
30729                 }
30730             }
30731             
30732             return;
30733         }
30734         
30735         width = this.thumbEl.getWidth();
30736         this.baseScale = width / this.imageEl.OriginWidth;
30737         
30738         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30739             height = this.thumbEl.getHeight();
30740             this.baseScale = height / this.imageEl.OriginHeight;
30741         }
30742         
30743         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30744             
30745             height = this.thumbEl.getHeight();
30746             this.baseScale = height / this.imageEl.OriginHeight;
30747             
30748             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30749                 width = this.thumbEl.getWidth();
30750                 this.baseScale = width / this.imageEl.OriginWidth;
30751             }
30752             
30753         }
30754         
30755         return;
30756     },
30757     
30758     getScaleLevel : function()
30759     {
30760         return this.baseScale * Math.pow(1.1, this.scale);
30761     },
30762     
30763     onTouchStart : function(e)
30764     {
30765         if(!this.canvasLoaded){
30766             this.beforeSelectFile(e);
30767             return;
30768         }
30769         
30770         var touches = e.browserEvent.touches;
30771         
30772         if(!touches){
30773             return;
30774         }
30775         
30776         if(touches.length == 1){
30777             this.onMouseDown(e);
30778             return;
30779         }
30780         
30781         if(touches.length != 2){
30782             return;
30783         }
30784         
30785         var coords = [];
30786         
30787         for(var i = 0, finger; finger = touches[i]; i++){
30788             coords.push(finger.pageX, finger.pageY);
30789         }
30790         
30791         var x = Math.pow(coords[0] - coords[2], 2);
30792         var y = Math.pow(coords[1] - coords[3], 2);
30793         
30794         this.startDistance = Math.sqrt(x + y);
30795         
30796         this.startScale = this.scale;
30797         
30798         this.pinching = true;
30799         this.dragable = false;
30800         
30801     },
30802     
30803     onTouchMove : function(e)
30804     {
30805         if(!this.pinching && !this.dragable){
30806             return;
30807         }
30808         
30809         var touches = e.browserEvent.touches;
30810         
30811         if(!touches){
30812             return;
30813         }
30814         
30815         if(this.dragable){
30816             this.onMouseMove(e);
30817             return;
30818         }
30819         
30820         var coords = [];
30821         
30822         for(var i = 0, finger; finger = touches[i]; i++){
30823             coords.push(finger.pageX, finger.pageY);
30824         }
30825         
30826         var x = Math.pow(coords[0] - coords[2], 2);
30827         var y = Math.pow(coords[1] - coords[3], 2);
30828         
30829         this.endDistance = Math.sqrt(x + y);
30830         
30831         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30832         
30833         if(!this.zoomable()){
30834             this.scale = this.startScale;
30835             return;
30836         }
30837         
30838         this.draw();
30839         
30840     },
30841     
30842     onTouchEnd : function(e)
30843     {
30844         this.pinching = false;
30845         this.dragable = false;
30846         
30847     },
30848     
30849     process : function(file, crop)
30850     {
30851         if(this.loadMask){
30852             this.maskEl.mask(this.loadingText);
30853         }
30854         
30855         this.xhr = new XMLHttpRequest();
30856         
30857         file.xhr = this.xhr;
30858
30859         this.xhr.open(this.method, this.url, true);
30860         
30861         var headers = {
30862             "Accept": "application/json",
30863             "Cache-Control": "no-cache",
30864             "X-Requested-With": "XMLHttpRequest"
30865         };
30866         
30867         for (var headerName in headers) {
30868             var headerValue = headers[headerName];
30869             if (headerValue) {
30870                 this.xhr.setRequestHeader(headerName, headerValue);
30871             }
30872         }
30873         
30874         var _this = this;
30875         
30876         this.xhr.onload = function()
30877         {
30878             _this.xhrOnLoad(_this.xhr);
30879         }
30880         
30881         this.xhr.onerror = function()
30882         {
30883             _this.xhrOnError(_this.xhr);
30884         }
30885         
30886         var formData = new FormData();
30887
30888         formData.append('returnHTML', 'NO');
30889         
30890         if(crop){
30891             formData.append('crop', crop);
30892         }
30893         
30894         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30895             formData.append(this.paramName, file, file.name);
30896         }
30897         
30898         if(typeof(file.filename) != 'undefined'){
30899             formData.append('filename', file.filename);
30900         }
30901         
30902         if(typeof(file.mimetype) != 'undefined'){
30903             formData.append('mimetype', file.mimetype);
30904         }
30905         
30906         if(this.fireEvent('arrange', this, formData) != false){
30907             this.xhr.send(formData);
30908         };
30909     },
30910     
30911     xhrOnLoad : function(xhr)
30912     {
30913         if(this.loadMask){
30914             this.maskEl.unmask();
30915         }
30916         
30917         if (xhr.readyState !== 4) {
30918             this.fireEvent('exception', this, xhr);
30919             return;
30920         }
30921
30922         var response = Roo.decode(xhr.responseText);
30923         
30924         if(!response.success){
30925             this.fireEvent('exception', this, xhr);
30926             return;
30927         }
30928         
30929         var response = Roo.decode(xhr.responseText);
30930         
30931         this.fireEvent('upload', this, response);
30932         
30933     },
30934     
30935     xhrOnError : function()
30936     {
30937         if(this.loadMask){
30938             this.maskEl.unmask();
30939         }
30940         
30941         Roo.log('xhr on error');
30942         
30943         var response = Roo.decode(xhr.responseText);
30944           
30945         Roo.log(response);
30946         
30947     },
30948     
30949     prepare : function(file)
30950     {   
30951         if(this.loadMask){
30952             this.maskEl.mask(this.loadingText);
30953         }
30954         
30955         this.file = false;
30956         this.exif = {};
30957         
30958         if(typeof(file) === 'string'){
30959             this.loadCanvas(file);
30960             return;
30961         }
30962         
30963         if(!file || !this.urlAPI){
30964             return;
30965         }
30966         
30967         this.file = file;
30968         this.cropType = file.type;
30969         
30970         var _this = this;
30971         
30972         if(this.fireEvent('prepare', this, this.file) != false){
30973             
30974             var reader = new FileReader();
30975             
30976             reader.onload = function (e) {
30977                 if (e.target.error) {
30978                     Roo.log(e.target.error);
30979                     return;
30980                 }
30981                 
30982                 var buffer = e.target.result,
30983                     dataView = new DataView(buffer),
30984                     offset = 2,
30985                     maxOffset = dataView.byteLength - 4,
30986                     markerBytes,
30987                     markerLength;
30988                 
30989                 if (dataView.getUint16(0) === 0xffd8) {
30990                     while (offset < maxOffset) {
30991                         markerBytes = dataView.getUint16(offset);
30992                         
30993                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30994                             markerLength = dataView.getUint16(offset + 2) + 2;
30995                             if (offset + markerLength > dataView.byteLength) {
30996                                 Roo.log('Invalid meta data: Invalid segment size.');
30997                                 break;
30998                             }
30999                             
31000                             if(markerBytes == 0xffe1){
31001                                 _this.parseExifData(
31002                                     dataView,
31003                                     offset,
31004                                     markerLength
31005                                 );
31006                             }
31007                             
31008                             offset += markerLength;
31009                             
31010                             continue;
31011                         }
31012                         
31013                         break;
31014                     }
31015                     
31016                 }
31017                 
31018                 var url = _this.urlAPI.createObjectURL(_this.file);
31019                 
31020                 _this.loadCanvas(url);
31021                 
31022                 return;
31023             }
31024             
31025             reader.readAsArrayBuffer(this.file);
31026             
31027         }
31028         
31029     },
31030     
31031     parseExifData : function(dataView, offset, length)
31032     {
31033         var tiffOffset = offset + 10,
31034             littleEndian,
31035             dirOffset;
31036     
31037         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31038             // No Exif data, might be XMP data instead
31039             return;
31040         }
31041         
31042         // Check for the ASCII code for "Exif" (0x45786966):
31043         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31044             // No Exif data, might be XMP data instead
31045             return;
31046         }
31047         if (tiffOffset + 8 > dataView.byteLength) {
31048             Roo.log('Invalid Exif data: Invalid segment size.');
31049             return;
31050         }
31051         // Check for the two null bytes:
31052         if (dataView.getUint16(offset + 8) !== 0x0000) {
31053             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31054             return;
31055         }
31056         // Check the byte alignment:
31057         switch (dataView.getUint16(tiffOffset)) {
31058         case 0x4949:
31059             littleEndian = true;
31060             break;
31061         case 0x4D4D:
31062             littleEndian = false;
31063             break;
31064         default:
31065             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31066             return;
31067         }
31068         // Check for the TIFF tag marker (0x002A):
31069         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31070             Roo.log('Invalid Exif data: Missing TIFF marker.');
31071             return;
31072         }
31073         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31074         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31075         
31076         this.parseExifTags(
31077             dataView,
31078             tiffOffset,
31079             tiffOffset + dirOffset,
31080             littleEndian
31081         );
31082     },
31083     
31084     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31085     {
31086         var tagsNumber,
31087             dirEndOffset,
31088             i;
31089         if (dirOffset + 6 > dataView.byteLength) {
31090             Roo.log('Invalid Exif data: Invalid directory offset.');
31091             return;
31092         }
31093         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31094         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31095         if (dirEndOffset + 4 > dataView.byteLength) {
31096             Roo.log('Invalid Exif data: Invalid directory size.');
31097             return;
31098         }
31099         for (i = 0; i < tagsNumber; i += 1) {
31100             this.parseExifTag(
31101                 dataView,
31102                 tiffOffset,
31103                 dirOffset + 2 + 12 * i, // tag offset
31104                 littleEndian
31105             );
31106         }
31107         // Return the offset to the next directory:
31108         return dataView.getUint32(dirEndOffset, littleEndian);
31109     },
31110     
31111     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31112     {
31113         var tag = dataView.getUint16(offset, littleEndian);
31114         
31115         this.exif[tag] = this.getExifValue(
31116             dataView,
31117             tiffOffset,
31118             offset,
31119             dataView.getUint16(offset + 2, littleEndian), // tag type
31120             dataView.getUint32(offset + 4, littleEndian), // tag length
31121             littleEndian
31122         );
31123     },
31124     
31125     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31126     {
31127         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31128             tagSize,
31129             dataOffset,
31130             values,
31131             i,
31132             str,
31133             c;
31134     
31135         if (!tagType) {
31136             Roo.log('Invalid Exif data: Invalid tag type.');
31137             return;
31138         }
31139         
31140         tagSize = tagType.size * length;
31141         // Determine if the value is contained in the dataOffset bytes,
31142         // or if the value at the dataOffset is a pointer to the actual data:
31143         dataOffset = tagSize > 4 ?
31144                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31145         if (dataOffset + tagSize > dataView.byteLength) {
31146             Roo.log('Invalid Exif data: Invalid data offset.');
31147             return;
31148         }
31149         if (length === 1) {
31150             return tagType.getValue(dataView, dataOffset, littleEndian);
31151         }
31152         values = [];
31153         for (i = 0; i < length; i += 1) {
31154             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31155         }
31156         
31157         if (tagType.ascii) {
31158             str = '';
31159             // Concatenate the chars:
31160             for (i = 0; i < values.length; i += 1) {
31161                 c = values[i];
31162                 // Ignore the terminating NULL byte(s):
31163                 if (c === '\u0000') {
31164                     break;
31165                 }
31166                 str += c;
31167             }
31168             return str;
31169         }
31170         return values;
31171     }
31172     
31173 });
31174
31175 Roo.apply(Roo.bootstrap.UploadCropbox, {
31176     tags : {
31177         'Orientation': 0x0112
31178     },
31179     
31180     Orientation: {
31181             1: 0, //'top-left',
31182 //            2: 'top-right',
31183             3: 180, //'bottom-right',
31184 //            4: 'bottom-left',
31185 //            5: 'left-top',
31186             6: 90, //'right-top',
31187 //            7: 'right-bottom',
31188             8: 270 //'left-bottom'
31189     },
31190     
31191     exifTagTypes : {
31192         // byte, 8-bit unsigned int:
31193         1: {
31194             getValue: function (dataView, dataOffset) {
31195                 return dataView.getUint8(dataOffset);
31196             },
31197             size: 1
31198         },
31199         // ascii, 8-bit byte:
31200         2: {
31201             getValue: function (dataView, dataOffset) {
31202                 return String.fromCharCode(dataView.getUint8(dataOffset));
31203             },
31204             size: 1,
31205             ascii: true
31206         },
31207         // short, 16 bit int:
31208         3: {
31209             getValue: function (dataView, dataOffset, littleEndian) {
31210                 return dataView.getUint16(dataOffset, littleEndian);
31211             },
31212             size: 2
31213         },
31214         // long, 32 bit int:
31215         4: {
31216             getValue: function (dataView, dataOffset, littleEndian) {
31217                 return dataView.getUint32(dataOffset, littleEndian);
31218             },
31219             size: 4
31220         },
31221         // rational = two long values, first is numerator, second is denominator:
31222         5: {
31223             getValue: function (dataView, dataOffset, littleEndian) {
31224                 return dataView.getUint32(dataOffset, littleEndian) /
31225                     dataView.getUint32(dataOffset + 4, littleEndian);
31226             },
31227             size: 8
31228         },
31229         // slong, 32 bit signed int:
31230         9: {
31231             getValue: function (dataView, dataOffset, littleEndian) {
31232                 return dataView.getInt32(dataOffset, littleEndian);
31233             },
31234             size: 4
31235         },
31236         // srational, two slongs, first is numerator, second is denominator:
31237         10: {
31238             getValue: function (dataView, dataOffset, littleEndian) {
31239                 return dataView.getInt32(dataOffset, littleEndian) /
31240                     dataView.getInt32(dataOffset + 4, littleEndian);
31241             },
31242             size: 8
31243         }
31244     },
31245     
31246     footer : {
31247         STANDARD : [
31248             {
31249                 tag : 'div',
31250                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31251                 action : 'rotate-left',
31252                 cn : [
31253                     {
31254                         tag : 'button',
31255                         cls : 'btn btn-default',
31256                         html : '<i class="fa fa-undo"></i>'
31257                     }
31258                 ]
31259             },
31260             {
31261                 tag : 'div',
31262                 cls : 'btn-group roo-upload-cropbox-picture',
31263                 action : 'picture',
31264                 cn : [
31265                     {
31266                         tag : 'button',
31267                         cls : 'btn btn-default',
31268                         html : '<i class="fa fa-picture-o"></i>'
31269                     }
31270                 ]
31271             },
31272             {
31273                 tag : 'div',
31274                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31275                 action : 'rotate-right',
31276                 cn : [
31277                     {
31278                         tag : 'button',
31279                         cls : 'btn btn-default',
31280                         html : '<i class="fa fa-repeat"></i>'
31281                     }
31282                 ]
31283             }
31284         ],
31285         DOCUMENT : [
31286             {
31287                 tag : 'div',
31288                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31289                 action : 'rotate-left',
31290                 cn : [
31291                     {
31292                         tag : 'button',
31293                         cls : 'btn btn-default',
31294                         html : '<i class="fa fa-undo"></i>'
31295                     }
31296                 ]
31297             },
31298             {
31299                 tag : 'div',
31300                 cls : 'btn-group roo-upload-cropbox-download',
31301                 action : 'download',
31302                 cn : [
31303                     {
31304                         tag : 'button',
31305                         cls : 'btn btn-default',
31306                         html : '<i class="fa fa-download"></i>'
31307                     }
31308                 ]
31309             },
31310             {
31311                 tag : 'div',
31312                 cls : 'btn-group roo-upload-cropbox-crop',
31313                 action : 'crop',
31314                 cn : [
31315                     {
31316                         tag : 'button',
31317                         cls : 'btn btn-default',
31318                         html : '<i class="fa fa-crop"></i>'
31319                     }
31320                 ]
31321             },
31322             {
31323                 tag : 'div',
31324                 cls : 'btn-group roo-upload-cropbox-trash',
31325                 action : 'trash',
31326                 cn : [
31327                     {
31328                         tag : 'button',
31329                         cls : 'btn btn-default',
31330                         html : '<i class="fa fa-trash"></i>'
31331                     }
31332                 ]
31333             },
31334             {
31335                 tag : 'div',
31336                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31337                 action : 'rotate-right',
31338                 cn : [
31339                     {
31340                         tag : 'button',
31341                         cls : 'btn btn-default',
31342                         html : '<i class="fa fa-repeat"></i>'
31343                     }
31344                 ]
31345             }
31346         ],
31347         ROTATOR : [
31348             {
31349                 tag : 'div',
31350                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31351                 action : 'rotate-left',
31352                 cn : [
31353                     {
31354                         tag : 'button',
31355                         cls : 'btn btn-default',
31356                         html : '<i class="fa fa-undo"></i>'
31357                     }
31358                 ]
31359             },
31360             {
31361                 tag : 'div',
31362                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31363                 action : 'rotate-right',
31364                 cn : [
31365                     {
31366                         tag : 'button',
31367                         cls : 'btn btn-default',
31368                         html : '<i class="fa fa-repeat"></i>'
31369                     }
31370                 ]
31371             }
31372         ]
31373     }
31374 });
31375
31376 /*
31377 * Licence: LGPL
31378 */
31379
31380 /**
31381  * @class Roo.bootstrap.DocumentManager
31382  * @extends Roo.bootstrap.Component
31383  * Bootstrap DocumentManager class
31384  * @cfg {String} paramName default 'imageUpload'
31385  * @cfg {String} toolTipName default 'filename'
31386  * @cfg {String} method default POST
31387  * @cfg {String} url action url
31388  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31389  * @cfg {Boolean} multiple multiple upload default true
31390  * @cfg {Number} thumbSize default 300
31391  * @cfg {String} fieldLabel
31392  * @cfg {Number} labelWidth default 4
31393  * @cfg {String} labelAlign (left|top) default left
31394  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31395 * @cfg {Number} labellg set the width of label (1-12)
31396  * @cfg {Number} labelmd set the width of label (1-12)
31397  * @cfg {Number} labelsm set the width of label (1-12)
31398  * @cfg {Number} labelxs set the width of label (1-12)
31399  * 
31400  * @constructor
31401  * Create a new DocumentManager
31402  * @param {Object} config The config object
31403  */
31404
31405 Roo.bootstrap.DocumentManager = function(config){
31406     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31407     
31408     this.files = [];
31409     this.delegates = [];
31410     
31411     this.addEvents({
31412         /**
31413          * @event initial
31414          * Fire when initial the DocumentManager
31415          * @param {Roo.bootstrap.DocumentManager} this
31416          */
31417         "initial" : true,
31418         /**
31419          * @event inspect
31420          * inspect selected file
31421          * @param {Roo.bootstrap.DocumentManager} this
31422          * @param {File} file
31423          */
31424         "inspect" : true,
31425         /**
31426          * @event exception
31427          * Fire when xhr load exception
31428          * @param {Roo.bootstrap.DocumentManager} this
31429          * @param {XMLHttpRequest} xhr
31430          */
31431         "exception" : true,
31432         /**
31433          * @event afterupload
31434          * Fire when xhr load exception
31435          * @param {Roo.bootstrap.DocumentManager} this
31436          * @param {XMLHttpRequest} xhr
31437          */
31438         "afterupload" : true,
31439         /**
31440          * @event prepare
31441          * prepare the form data
31442          * @param {Roo.bootstrap.DocumentManager} this
31443          * @param {Object} formData
31444          */
31445         "prepare" : true,
31446         /**
31447          * @event remove
31448          * Fire when remove the file
31449          * @param {Roo.bootstrap.DocumentManager} this
31450          * @param {Object} file
31451          */
31452         "remove" : true,
31453         /**
31454          * @event refresh
31455          * Fire after refresh the file
31456          * @param {Roo.bootstrap.DocumentManager} this
31457          */
31458         "refresh" : true,
31459         /**
31460          * @event click
31461          * Fire after click the image
31462          * @param {Roo.bootstrap.DocumentManager} this
31463          * @param {Object} file
31464          */
31465         "click" : true,
31466         /**
31467          * @event edit
31468          * Fire when upload a image and editable set to true
31469          * @param {Roo.bootstrap.DocumentManager} this
31470          * @param {Object} file
31471          */
31472         "edit" : true,
31473         /**
31474          * @event beforeselectfile
31475          * Fire before select file
31476          * @param {Roo.bootstrap.DocumentManager} this
31477          */
31478         "beforeselectfile" : true,
31479         /**
31480          * @event process
31481          * Fire before process file
31482          * @param {Roo.bootstrap.DocumentManager} this
31483          * @param {Object} file
31484          */
31485         "process" : true,
31486         /**
31487          * @event previewrendered
31488          * Fire when preview rendered
31489          * @param {Roo.bootstrap.DocumentManager} this
31490          * @param {Object} file
31491          */
31492         "previewrendered" : true,
31493         /**
31494          */
31495         "previewResize" : true
31496         
31497     });
31498 };
31499
31500 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31501     
31502     boxes : 0,
31503     inputName : '',
31504     thumbSize : 300,
31505     multiple : true,
31506     files : false,
31507     method : 'POST',
31508     url : '',
31509     paramName : 'imageUpload',
31510     toolTipName : 'filename',
31511     fieldLabel : '',
31512     labelWidth : 4,
31513     labelAlign : 'left',
31514     editable : true,
31515     delegates : false,
31516     xhr : false, 
31517     
31518     labellg : 0,
31519     labelmd : 0,
31520     labelsm : 0,
31521     labelxs : 0,
31522     
31523     getAutoCreate : function()
31524     {   
31525         var managerWidget = {
31526             tag : 'div',
31527             cls : 'roo-document-manager',
31528             cn : [
31529                 {
31530                     tag : 'input',
31531                     cls : 'roo-document-manager-selector',
31532                     type : 'file'
31533                 },
31534                 {
31535                     tag : 'div',
31536                     cls : 'roo-document-manager-uploader',
31537                     cn : [
31538                         {
31539                             tag : 'div',
31540                             cls : 'roo-document-manager-upload-btn',
31541                             html : '<i class="fa fa-plus"></i>'
31542                         }
31543                     ]
31544                     
31545                 }
31546             ]
31547         };
31548         
31549         var content = [
31550             {
31551                 tag : 'div',
31552                 cls : 'column col-md-12',
31553                 cn : managerWidget
31554             }
31555         ];
31556         
31557         if(this.fieldLabel.length){
31558             
31559             content = [
31560                 {
31561                     tag : 'div',
31562                     cls : 'column col-md-12',
31563                     html : this.fieldLabel
31564                 },
31565                 {
31566                     tag : 'div',
31567                     cls : 'column col-md-12',
31568                     cn : managerWidget
31569                 }
31570             ];
31571
31572             if(this.labelAlign == 'left'){
31573                 content = [
31574                     {
31575                         tag : 'div',
31576                         cls : 'column',
31577                         html : this.fieldLabel
31578                     },
31579                     {
31580                         tag : 'div',
31581                         cls : 'column',
31582                         cn : managerWidget
31583                     }
31584                 ];
31585                 
31586                 if(this.labelWidth > 12){
31587                     content[0].style = "width: " + this.labelWidth + 'px';
31588                 }
31589
31590                 if(this.labelWidth < 13 && this.labelmd == 0){
31591                     this.labelmd = this.labelWidth;
31592                 }
31593
31594                 if(this.labellg > 0){
31595                     content[0].cls += ' col-lg-' + this.labellg;
31596                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31597                 }
31598
31599                 if(this.labelmd > 0){
31600                     content[0].cls += ' col-md-' + this.labelmd;
31601                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31602                 }
31603
31604                 if(this.labelsm > 0){
31605                     content[0].cls += ' col-sm-' + this.labelsm;
31606                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31607                 }
31608
31609                 if(this.labelxs > 0){
31610                     content[0].cls += ' col-xs-' + this.labelxs;
31611                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31612                 }
31613                 
31614             }
31615         }
31616         
31617         var cfg = {
31618             tag : 'div',
31619             cls : 'row clearfix',
31620             cn : content
31621         };
31622         
31623         return cfg;
31624         
31625     },
31626     
31627     initEvents : function()
31628     {
31629         this.managerEl = this.el.select('.roo-document-manager', true).first();
31630         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31631         
31632         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31633         this.selectorEl.hide();
31634         
31635         if(this.multiple){
31636             this.selectorEl.attr('multiple', 'multiple');
31637         }
31638         
31639         this.selectorEl.on('change', this.onFileSelected, this);
31640         
31641         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31642         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31643         
31644         this.uploader.on('click', this.onUploaderClick, this);
31645         
31646         this.renderProgressDialog();
31647         
31648         var _this = this;
31649         
31650         window.addEventListener("resize", function() { _this.refresh(); } );
31651         
31652         this.fireEvent('initial', this);
31653     },
31654     
31655     renderProgressDialog : function()
31656     {
31657         var _this = this;
31658         
31659         this.progressDialog = new Roo.bootstrap.Modal({
31660             cls : 'roo-document-manager-progress-dialog',
31661             allow_close : false,
31662             animate : false,
31663             title : '',
31664             buttons : [
31665                 {
31666                     name  :'cancel',
31667                     weight : 'danger',
31668                     html : 'Cancel'
31669                 }
31670             ], 
31671             listeners : { 
31672                 btnclick : function() {
31673                     _this.uploadCancel();
31674                     this.hide();
31675                 }
31676             }
31677         });
31678          
31679         this.progressDialog.render(Roo.get(document.body));
31680          
31681         this.progress = new Roo.bootstrap.Progress({
31682             cls : 'roo-document-manager-progress',
31683             active : true,
31684             striped : true
31685         });
31686         
31687         this.progress.render(this.progressDialog.getChildContainer());
31688         
31689         this.progressBar = new Roo.bootstrap.ProgressBar({
31690             cls : 'roo-document-manager-progress-bar',
31691             aria_valuenow : 0,
31692             aria_valuemin : 0,
31693             aria_valuemax : 12,
31694             panel : 'success'
31695         });
31696         
31697         this.progressBar.render(this.progress.getChildContainer());
31698     },
31699     
31700     onUploaderClick : function(e)
31701     {
31702         e.preventDefault();
31703      
31704         if(this.fireEvent('beforeselectfile', this) != false){
31705             this.selectorEl.dom.click();
31706         }
31707         
31708     },
31709     
31710     onFileSelected : function(e)
31711     {
31712         e.preventDefault();
31713         
31714         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31715             return;
31716         }
31717         
31718         Roo.each(this.selectorEl.dom.files, function(file){
31719             if(this.fireEvent('inspect', this, file) != false){
31720                 this.files.push(file);
31721             }
31722         }, this);
31723         
31724         this.queue();
31725         
31726     },
31727     
31728     queue : function()
31729     {
31730         this.selectorEl.dom.value = '';
31731         
31732         if(!this.files || !this.files.length){
31733             return;
31734         }
31735         
31736         if(this.boxes > 0 && this.files.length > this.boxes){
31737             this.files = this.files.slice(0, this.boxes);
31738         }
31739         
31740         this.uploader.show();
31741         
31742         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31743             this.uploader.hide();
31744         }
31745         
31746         var _this = this;
31747         
31748         var files = [];
31749         
31750         var docs = [];
31751         
31752         Roo.each(this.files, function(file){
31753             
31754             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31755                 var f = this.renderPreview(file);
31756                 files.push(f);
31757                 return;
31758             }
31759             
31760             if(file.type.indexOf('image') != -1){
31761                 this.delegates.push(
31762                     (function(){
31763                         _this.process(file);
31764                     }).createDelegate(this)
31765                 );
31766         
31767                 return;
31768             }
31769             
31770             docs.push(
31771                 (function(){
31772                     _this.process(file);
31773                 }).createDelegate(this)
31774             );
31775             
31776         }, this);
31777         
31778         this.files = files;
31779         
31780         this.delegates = this.delegates.concat(docs);
31781         
31782         if(!this.delegates.length){
31783             this.refresh();
31784             return;
31785         }
31786         
31787         this.progressBar.aria_valuemax = this.delegates.length;
31788         
31789         this.arrange();
31790         
31791         return;
31792     },
31793     
31794     arrange : function()
31795     {
31796         if(!this.delegates.length){
31797             this.progressDialog.hide();
31798             this.refresh();
31799             return;
31800         }
31801         
31802         var delegate = this.delegates.shift();
31803         
31804         this.progressDialog.show();
31805         
31806         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31807         
31808         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31809         
31810         delegate();
31811     },
31812     
31813     refresh : function()
31814     {
31815         this.uploader.show();
31816         
31817         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31818             this.uploader.hide();
31819         }
31820         
31821         Roo.isTouch ? this.closable(false) : this.closable(true);
31822         
31823         this.fireEvent('refresh', this);
31824     },
31825     
31826     onRemove : function(e, el, o)
31827     {
31828         e.preventDefault();
31829         
31830         this.fireEvent('remove', this, o);
31831         
31832     },
31833     
31834     remove : function(o)
31835     {
31836         var files = [];
31837         
31838         Roo.each(this.files, function(file){
31839             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31840                 files.push(file);
31841                 return;
31842             }
31843
31844             o.target.remove();
31845
31846         }, this);
31847         
31848         this.files = files;
31849         
31850         this.refresh();
31851     },
31852     
31853     clear : function()
31854     {
31855         Roo.each(this.files, function(file){
31856             if(!file.target){
31857                 return;
31858             }
31859             
31860             file.target.remove();
31861
31862         }, this);
31863         
31864         this.files = [];
31865         
31866         this.refresh();
31867     },
31868     
31869     onClick : function(e, el, o)
31870     {
31871         e.preventDefault();
31872         
31873         this.fireEvent('click', this, o);
31874         
31875     },
31876     
31877     closable : function(closable)
31878     {
31879         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31880             
31881             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31882             
31883             if(closable){
31884                 el.show();
31885                 return;
31886             }
31887             
31888             el.hide();
31889             
31890         }, this);
31891     },
31892     
31893     xhrOnLoad : function(xhr)
31894     {
31895         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31896             el.remove();
31897         }, this);
31898         
31899         if (xhr.readyState !== 4) {
31900             this.arrange();
31901             this.fireEvent('exception', this, xhr);
31902             return;
31903         }
31904
31905         var response = Roo.decode(xhr.responseText);
31906         
31907         if(!response.success){
31908             this.arrange();
31909             this.fireEvent('exception', this, xhr);
31910             return;
31911         }
31912         
31913         var file = this.renderPreview(response.data);
31914         
31915         this.files.push(file);
31916         
31917         this.arrange();
31918         
31919         this.fireEvent('afterupload', this, xhr);
31920         
31921     },
31922     
31923     xhrOnError : function(xhr)
31924     {
31925         Roo.log('xhr on error');
31926         
31927         var response = Roo.decode(xhr.responseText);
31928           
31929         Roo.log(response);
31930         
31931         this.arrange();
31932     },
31933     
31934     process : function(file)
31935     {
31936         if(this.fireEvent('process', this, file) !== false){
31937             if(this.editable && file.type.indexOf('image') != -1){
31938                 this.fireEvent('edit', this, file);
31939                 return;
31940             }
31941
31942             this.uploadStart(file, false);
31943
31944             return;
31945         }
31946         
31947     },
31948     
31949     uploadStart : function(file, crop)
31950     {
31951         this.xhr = new XMLHttpRequest();
31952         
31953         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31954             this.arrange();
31955             return;
31956         }
31957         
31958         file.xhr = this.xhr;
31959             
31960         this.managerEl.createChild({
31961             tag : 'div',
31962             cls : 'roo-document-manager-loading',
31963             cn : [
31964                 {
31965                     tag : 'div',
31966                     tooltip : file.name,
31967                     cls : 'roo-document-manager-thumb',
31968                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31969                 }
31970             ]
31971
31972         });
31973
31974         this.xhr.open(this.method, this.url, true);
31975         
31976         var headers = {
31977             "Accept": "application/json",
31978             "Cache-Control": "no-cache",
31979             "X-Requested-With": "XMLHttpRequest"
31980         };
31981         
31982         for (var headerName in headers) {
31983             var headerValue = headers[headerName];
31984             if (headerValue) {
31985                 this.xhr.setRequestHeader(headerName, headerValue);
31986             }
31987         }
31988         
31989         var _this = this;
31990         
31991         this.xhr.onload = function()
31992         {
31993             _this.xhrOnLoad(_this.xhr);
31994         }
31995         
31996         this.xhr.onerror = function()
31997         {
31998             _this.xhrOnError(_this.xhr);
31999         }
32000         
32001         var formData = new FormData();
32002
32003         formData.append('returnHTML', 'NO');
32004         
32005         if(crop){
32006             formData.append('crop', crop);
32007         }
32008         
32009         formData.append(this.paramName, file, file.name);
32010         
32011         var options = {
32012             file : file, 
32013             manually : false
32014         };
32015         
32016         if(this.fireEvent('prepare', this, formData, options) != false){
32017             
32018             if(options.manually){
32019                 return;
32020             }
32021             
32022             this.xhr.send(formData);
32023             return;
32024         };
32025         
32026         this.uploadCancel();
32027     },
32028     
32029     uploadCancel : function()
32030     {
32031         if (this.xhr) {
32032             this.xhr.abort();
32033         }
32034         
32035         this.delegates = [];
32036         
32037         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32038             el.remove();
32039         }, this);
32040         
32041         this.arrange();
32042     },
32043     
32044     renderPreview : function(file)
32045     {
32046         if(typeof(file.target) != 'undefined' && file.target){
32047             return file;
32048         }
32049         
32050         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32051         
32052         var previewEl = this.managerEl.createChild({
32053             tag : 'div',
32054             cls : 'roo-document-manager-preview',
32055             cn : [
32056                 {
32057                     tag : 'div',
32058                     tooltip : file[this.toolTipName],
32059                     cls : 'roo-document-manager-thumb',
32060                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32061                 },
32062                 {
32063                     tag : 'button',
32064                     cls : 'close',
32065                     html : '<i class="fa fa-times-circle"></i>'
32066                 }
32067             ]
32068         });
32069
32070         var close = previewEl.select('button.close', true).first();
32071
32072         close.on('click', this.onRemove, this, file);
32073
32074         file.target = previewEl;
32075
32076         var image = previewEl.select('img', true).first();
32077         
32078         var _this = this;
32079         
32080         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32081         
32082         image.on('click', this.onClick, this, file);
32083         
32084         this.fireEvent('previewrendered', this, file);
32085         
32086         return file;
32087         
32088     },
32089     
32090     onPreviewLoad : function(file, image)
32091     {
32092         if(typeof(file.target) == 'undefined' || !file.target){
32093             return;
32094         }
32095         
32096         var width = image.dom.naturalWidth || image.dom.width;
32097         var height = image.dom.naturalHeight || image.dom.height;
32098         
32099         if(!this.previewResize) {
32100             return;
32101         }
32102         
32103         if(width > height){
32104             file.target.addClass('wide');
32105             return;
32106         }
32107         
32108         file.target.addClass('tall');
32109         return;
32110         
32111     },
32112     
32113     uploadFromSource : function(file, crop)
32114     {
32115         this.xhr = new XMLHttpRequest();
32116         
32117         this.managerEl.createChild({
32118             tag : 'div',
32119             cls : 'roo-document-manager-loading',
32120             cn : [
32121                 {
32122                     tag : 'div',
32123                     tooltip : file.name,
32124                     cls : 'roo-document-manager-thumb',
32125                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32126                 }
32127             ]
32128
32129         });
32130
32131         this.xhr.open(this.method, this.url, true);
32132         
32133         var headers = {
32134             "Accept": "application/json",
32135             "Cache-Control": "no-cache",
32136             "X-Requested-With": "XMLHttpRequest"
32137         };
32138         
32139         for (var headerName in headers) {
32140             var headerValue = headers[headerName];
32141             if (headerValue) {
32142                 this.xhr.setRequestHeader(headerName, headerValue);
32143             }
32144         }
32145         
32146         var _this = this;
32147         
32148         this.xhr.onload = function()
32149         {
32150             _this.xhrOnLoad(_this.xhr);
32151         }
32152         
32153         this.xhr.onerror = function()
32154         {
32155             _this.xhrOnError(_this.xhr);
32156         }
32157         
32158         var formData = new FormData();
32159
32160         formData.append('returnHTML', 'NO');
32161         
32162         formData.append('crop', crop);
32163         
32164         if(typeof(file.filename) != 'undefined'){
32165             formData.append('filename', file.filename);
32166         }
32167         
32168         if(typeof(file.mimetype) != 'undefined'){
32169             formData.append('mimetype', file.mimetype);
32170         }
32171         
32172         Roo.log(formData);
32173         
32174         if(this.fireEvent('prepare', this, formData) != false){
32175             this.xhr.send(formData);
32176         };
32177     }
32178 });
32179
32180 /*
32181 * Licence: LGPL
32182 */
32183
32184 /**
32185  * @class Roo.bootstrap.DocumentViewer
32186  * @extends Roo.bootstrap.Component
32187  * Bootstrap DocumentViewer class
32188  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32189  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32190  * 
32191  * @constructor
32192  * Create a new DocumentViewer
32193  * @param {Object} config The config object
32194  */
32195
32196 Roo.bootstrap.DocumentViewer = function(config){
32197     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32198     
32199     this.addEvents({
32200         /**
32201          * @event initial
32202          * Fire after initEvent
32203          * @param {Roo.bootstrap.DocumentViewer} this
32204          */
32205         "initial" : true,
32206         /**
32207          * @event click
32208          * Fire after click
32209          * @param {Roo.bootstrap.DocumentViewer} this
32210          */
32211         "click" : true,
32212         /**
32213          * @event download
32214          * Fire after download button
32215          * @param {Roo.bootstrap.DocumentViewer} this
32216          */
32217         "download" : true,
32218         /**
32219          * @event trash
32220          * Fire after trash button
32221          * @param {Roo.bootstrap.DocumentViewer} this
32222          */
32223         "trash" : true
32224         
32225     });
32226 };
32227
32228 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32229     
32230     showDownload : true,
32231     
32232     showTrash : true,
32233     
32234     getAutoCreate : function()
32235     {
32236         var cfg = {
32237             tag : 'div',
32238             cls : 'roo-document-viewer',
32239             cn : [
32240                 {
32241                     tag : 'div',
32242                     cls : 'roo-document-viewer-body',
32243                     cn : [
32244                         {
32245                             tag : 'div',
32246                             cls : 'roo-document-viewer-thumb',
32247                             cn : [
32248                                 {
32249                                     tag : 'img',
32250                                     cls : 'roo-document-viewer-image'
32251                                 }
32252                             ]
32253                         }
32254                     ]
32255                 },
32256                 {
32257                     tag : 'div',
32258                     cls : 'roo-document-viewer-footer',
32259                     cn : {
32260                         tag : 'div',
32261                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32262                         cn : [
32263                             {
32264                                 tag : 'div',
32265                                 cls : 'btn-group roo-document-viewer-download',
32266                                 cn : [
32267                                     {
32268                                         tag : 'button',
32269                                         cls : 'btn btn-default',
32270                                         html : '<i class="fa fa-download"></i>'
32271                                     }
32272                                 ]
32273                             },
32274                             {
32275                                 tag : 'div',
32276                                 cls : 'btn-group roo-document-viewer-trash',
32277                                 cn : [
32278                                     {
32279                                         tag : 'button',
32280                                         cls : 'btn btn-default',
32281                                         html : '<i class="fa fa-trash"></i>'
32282                                     }
32283                                 ]
32284                             }
32285                         ]
32286                     }
32287                 }
32288             ]
32289         };
32290         
32291         return cfg;
32292     },
32293     
32294     initEvents : function()
32295     {
32296         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32297         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32298         
32299         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32300         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32301         
32302         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32303         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32304         
32305         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32306         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32307         
32308         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32309         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32310         
32311         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32312         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32313         
32314         this.bodyEl.on('click', this.onClick, this);
32315         this.downloadBtn.on('click', this.onDownload, this);
32316         this.trashBtn.on('click', this.onTrash, this);
32317         
32318         this.downloadBtn.hide();
32319         this.trashBtn.hide();
32320         
32321         if(this.showDownload){
32322             this.downloadBtn.show();
32323         }
32324         
32325         if(this.showTrash){
32326             this.trashBtn.show();
32327         }
32328         
32329         if(!this.showDownload && !this.showTrash) {
32330             this.footerEl.hide();
32331         }
32332         
32333     },
32334     
32335     initial : function()
32336     {
32337         this.fireEvent('initial', this);
32338         
32339     },
32340     
32341     onClick : function(e)
32342     {
32343         e.preventDefault();
32344         
32345         this.fireEvent('click', this);
32346     },
32347     
32348     onDownload : function(e)
32349     {
32350         e.preventDefault();
32351         
32352         this.fireEvent('download', this);
32353     },
32354     
32355     onTrash : function(e)
32356     {
32357         e.preventDefault();
32358         
32359         this.fireEvent('trash', this);
32360     }
32361     
32362 });
32363 /*
32364  * - LGPL
32365  *
32366  * nav progress bar
32367  * 
32368  */
32369
32370 /**
32371  * @class Roo.bootstrap.NavProgressBar
32372  * @extends Roo.bootstrap.Component
32373  * Bootstrap NavProgressBar class
32374  * 
32375  * @constructor
32376  * Create a new nav progress bar
32377  * @param {Object} config The config object
32378  */
32379
32380 Roo.bootstrap.NavProgressBar = function(config){
32381     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32382
32383     this.bullets = this.bullets || [];
32384    
32385 //    Roo.bootstrap.NavProgressBar.register(this);
32386      this.addEvents({
32387         /**
32388              * @event changed
32389              * Fires when the active item changes
32390              * @param {Roo.bootstrap.NavProgressBar} this
32391              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32392              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32393          */
32394         'changed': true
32395      });
32396     
32397 };
32398
32399 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32400     
32401     bullets : [],
32402     barItems : [],
32403     
32404     getAutoCreate : function()
32405     {
32406         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32407         
32408         cfg = {
32409             tag : 'div',
32410             cls : 'roo-navigation-bar-group',
32411             cn : [
32412                 {
32413                     tag : 'div',
32414                     cls : 'roo-navigation-top-bar'
32415                 },
32416                 {
32417                     tag : 'div',
32418                     cls : 'roo-navigation-bullets-bar',
32419                     cn : [
32420                         {
32421                             tag : 'ul',
32422                             cls : 'roo-navigation-bar'
32423                         }
32424                     ]
32425                 },
32426                 
32427                 {
32428                     tag : 'div',
32429                     cls : 'roo-navigation-bottom-bar'
32430                 }
32431             ]
32432             
32433         };
32434         
32435         return cfg;
32436         
32437     },
32438     
32439     initEvents: function() 
32440     {
32441         
32442     },
32443     
32444     onRender : function(ct, position) 
32445     {
32446         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32447         
32448         if(this.bullets.length){
32449             Roo.each(this.bullets, function(b){
32450                this.addItem(b);
32451             }, this);
32452         }
32453         
32454         this.format();
32455         
32456     },
32457     
32458     addItem : function(cfg)
32459     {
32460         var item = new Roo.bootstrap.NavProgressItem(cfg);
32461         
32462         item.parentId = this.id;
32463         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32464         
32465         if(cfg.html){
32466             var top = new Roo.bootstrap.Element({
32467                 tag : 'div',
32468                 cls : 'roo-navigation-bar-text'
32469             });
32470             
32471             var bottom = new Roo.bootstrap.Element({
32472                 tag : 'div',
32473                 cls : 'roo-navigation-bar-text'
32474             });
32475             
32476             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32477             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32478             
32479             var topText = new Roo.bootstrap.Element({
32480                 tag : 'span',
32481                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32482             });
32483             
32484             var bottomText = new Roo.bootstrap.Element({
32485                 tag : 'span',
32486                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32487             });
32488             
32489             topText.onRender(top.el, null);
32490             bottomText.onRender(bottom.el, null);
32491             
32492             item.topEl = top;
32493             item.bottomEl = bottom;
32494         }
32495         
32496         this.barItems.push(item);
32497         
32498         return item;
32499     },
32500     
32501     getActive : function()
32502     {
32503         var active = false;
32504         
32505         Roo.each(this.barItems, function(v){
32506             
32507             if (!v.isActive()) {
32508                 return;
32509             }
32510             
32511             active = v;
32512             return false;
32513             
32514         });
32515         
32516         return active;
32517     },
32518     
32519     setActiveItem : function(item)
32520     {
32521         var prev = false;
32522         
32523         Roo.each(this.barItems, function(v){
32524             if (v.rid == item.rid) {
32525                 return ;
32526             }
32527             
32528             if (v.isActive()) {
32529                 v.setActive(false);
32530                 prev = v;
32531             }
32532         });
32533
32534         item.setActive(true);
32535         
32536         this.fireEvent('changed', this, item, prev);
32537     },
32538     
32539     getBarItem: function(rid)
32540     {
32541         var ret = false;
32542         
32543         Roo.each(this.barItems, function(e) {
32544             if (e.rid != rid) {
32545                 return;
32546             }
32547             
32548             ret =  e;
32549             return false;
32550         });
32551         
32552         return ret;
32553     },
32554     
32555     indexOfItem : function(item)
32556     {
32557         var index = false;
32558         
32559         Roo.each(this.barItems, function(v, i){
32560             
32561             if (v.rid != item.rid) {
32562                 return;
32563             }
32564             
32565             index = i;
32566             return false
32567         });
32568         
32569         return index;
32570     },
32571     
32572     setActiveNext : function()
32573     {
32574         var i = this.indexOfItem(this.getActive());
32575         
32576         if (i > this.barItems.length) {
32577             return;
32578         }
32579         
32580         this.setActiveItem(this.barItems[i+1]);
32581     },
32582     
32583     setActivePrev : function()
32584     {
32585         var i = this.indexOfItem(this.getActive());
32586         
32587         if (i  < 1) {
32588             return;
32589         }
32590         
32591         this.setActiveItem(this.barItems[i-1]);
32592     },
32593     
32594     format : function()
32595     {
32596         if(!this.barItems.length){
32597             return;
32598         }
32599      
32600         var width = 100 / this.barItems.length;
32601         
32602         Roo.each(this.barItems, function(i){
32603             i.el.setStyle('width', width + '%');
32604             i.topEl.el.setStyle('width', width + '%');
32605             i.bottomEl.el.setStyle('width', width + '%');
32606         }, this);
32607         
32608     }
32609     
32610 });
32611 /*
32612  * - LGPL
32613  *
32614  * Nav Progress Item
32615  * 
32616  */
32617
32618 /**
32619  * @class Roo.bootstrap.NavProgressItem
32620  * @extends Roo.bootstrap.Component
32621  * Bootstrap NavProgressItem class
32622  * @cfg {String} rid the reference id
32623  * @cfg {Boolean} active (true|false) Is item active default false
32624  * @cfg {Boolean} disabled (true|false) Is item active default false
32625  * @cfg {String} html
32626  * @cfg {String} position (top|bottom) text position default bottom
32627  * @cfg {String} icon show icon instead of number
32628  * 
32629  * @constructor
32630  * Create a new NavProgressItem
32631  * @param {Object} config The config object
32632  */
32633 Roo.bootstrap.NavProgressItem = function(config){
32634     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32635     this.addEvents({
32636         // raw events
32637         /**
32638          * @event click
32639          * The raw click event for the entire grid.
32640          * @param {Roo.bootstrap.NavProgressItem} this
32641          * @param {Roo.EventObject} e
32642          */
32643         "click" : true
32644     });
32645    
32646 };
32647
32648 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32649     
32650     rid : '',
32651     active : false,
32652     disabled : false,
32653     html : '',
32654     position : 'bottom',
32655     icon : false,
32656     
32657     getAutoCreate : function()
32658     {
32659         var iconCls = 'roo-navigation-bar-item-icon';
32660         
32661         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32662         
32663         var cfg = {
32664             tag: 'li',
32665             cls: 'roo-navigation-bar-item',
32666             cn : [
32667                 {
32668                     tag : 'i',
32669                     cls : iconCls
32670                 }
32671             ]
32672         };
32673         
32674         if(this.active){
32675             cfg.cls += ' active';
32676         }
32677         if(this.disabled){
32678             cfg.cls += ' disabled';
32679         }
32680         
32681         return cfg;
32682     },
32683     
32684     disable : function()
32685     {
32686         this.setDisabled(true);
32687     },
32688     
32689     enable : function()
32690     {
32691         this.setDisabled(false);
32692     },
32693     
32694     initEvents: function() 
32695     {
32696         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32697         
32698         this.iconEl.on('click', this.onClick, this);
32699     },
32700     
32701     onClick : function(e)
32702     {
32703         e.preventDefault();
32704         
32705         if(this.disabled){
32706             return;
32707         }
32708         
32709         if(this.fireEvent('click', this, e) === false){
32710             return;
32711         };
32712         
32713         this.parent().setActiveItem(this);
32714     },
32715     
32716     isActive: function () 
32717     {
32718         return this.active;
32719     },
32720     
32721     setActive : function(state)
32722     {
32723         if(this.active == state){
32724             return;
32725         }
32726         
32727         this.active = state;
32728         
32729         if (state) {
32730             this.el.addClass('active');
32731             return;
32732         }
32733         
32734         this.el.removeClass('active');
32735         
32736         return;
32737     },
32738     
32739     setDisabled : function(state)
32740     {
32741         if(this.disabled == state){
32742             return;
32743         }
32744         
32745         this.disabled = state;
32746         
32747         if (state) {
32748             this.el.addClass('disabled');
32749             return;
32750         }
32751         
32752         this.el.removeClass('disabled');
32753     },
32754     
32755     tooltipEl : function()
32756     {
32757         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32758     }
32759 });
32760  
32761
32762  /*
32763  * - LGPL
32764  *
32765  * FieldLabel
32766  * 
32767  */
32768
32769 /**
32770  * @class Roo.bootstrap.FieldLabel
32771  * @extends Roo.bootstrap.Component
32772  * Bootstrap FieldLabel class
32773  * @cfg {String} html contents of the element
32774  * @cfg {String} tag tag of the element default label
32775  * @cfg {String} cls class of the element
32776  * @cfg {String} target label target 
32777  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32778  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32779  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32780  * @cfg {String} iconTooltip default "This field is required"
32781  * @cfg {String} indicatorpos (left|right) default left
32782  * 
32783  * @constructor
32784  * Create a new FieldLabel
32785  * @param {Object} config The config object
32786  */
32787
32788 Roo.bootstrap.FieldLabel = function(config){
32789     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32790     
32791     this.addEvents({
32792             /**
32793              * @event invalid
32794              * Fires after the field has been marked as invalid.
32795              * @param {Roo.form.FieldLabel} this
32796              * @param {String} msg The validation message
32797              */
32798             invalid : true,
32799             /**
32800              * @event valid
32801              * Fires after the field has been validated with no errors.
32802              * @param {Roo.form.FieldLabel} this
32803              */
32804             valid : true
32805         });
32806 };
32807
32808 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32809     
32810     tag: 'label',
32811     cls: '',
32812     html: '',
32813     target: '',
32814     allowBlank : true,
32815     invalidClass : 'has-warning',
32816     validClass : 'has-success',
32817     iconTooltip : 'This field is required',
32818     indicatorpos : 'left',
32819     
32820     getAutoCreate : function(){
32821         
32822         var cls = "";
32823         if (!this.allowBlank) {
32824             cls  = "visible";
32825         }
32826         
32827         var cfg = {
32828             tag : this.tag,
32829             cls : 'roo-bootstrap-field-label ' + this.cls,
32830             for : this.target,
32831             cn : [
32832                 {
32833                     tag : 'i',
32834                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32835                     tooltip : this.iconTooltip
32836                 },
32837                 {
32838                     tag : 'span',
32839                     html : this.html
32840                 }
32841             ] 
32842         };
32843         
32844         if(this.indicatorpos == 'right'){
32845             var cfg = {
32846                 tag : this.tag,
32847                 cls : 'roo-bootstrap-field-label ' + this.cls,
32848                 for : this.target,
32849                 cn : [
32850                     {
32851                         tag : 'span',
32852                         html : this.html
32853                     },
32854                     {
32855                         tag : 'i',
32856                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32857                         tooltip : this.iconTooltip
32858                     }
32859                 ] 
32860             };
32861         }
32862         
32863         return cfg;
32864     },
32865     
32866     initEvents: function() 
32867     {
32868         Roo.bootstrap.Element.superclass.initEvents.call(this);
32869         
32870         this.indicator = this.indicatorEl();
32871         
32872         if(this.indicator){
32873             this.indicator.removeClass('visible');
32874             this.indicator.addClass('invisible');
32875         }
32876         
32877         Roo.bootstrap.FieldLabel.register(this);
32878     },
32879     
32880     indicatorEl : function()
32881     {
32882         var indicator = this.el.select('i.roo-required-indicator',true).first();
32883         
32884         if(!indicator){
32885             return false;
32886         }
32887         
32888         return indicator;
32889         
32890     },
32891     
32892     /**
32893      * Mark this field as valid
32894      */
32895     markValid : function()
32896     {
32897         if(this.indicator){
32898             this.indicator.removeClass('visible');
32899             this.indicator.addClass('invisible');
32900         }
32901         if (Roo.bootstrap.version == 3) {
32902             this.el.removeClass(this.invalidClass);
32903             this.el.addClass(this.validClass);
32904         } else {
32905             this.el.removeClass('is-invalid');
32906             this.el.addClass('is-valid');
32907         }
32908         
32909         
32910         this.fireEvent('valid', this);
32911     },
32912     
32913     /**
32914      * Mark this field as invalid
32915      * @param {String} msg The validation message
32916      */
32917     markInvalid : function(msg)
32918     {
32919         if(this.indicator){
32920             this.indicator.removeClass('invisible');
32921             this.indicator.addClass('visible');
32922         }
32923           if (Roo.bootstrap.version == 3) {
32924             this.el.removeClass(this.validClass);
32925             this.el.addClass(this.invalidClass);
32926         } else {
32927             this.el.removeClass('is-valid');
32928             this.el.addClass('is-invalid');
32929         }
32930         
32931         
32932         this.fireEvent('invalid', this, msg);
32933     }
32934     
32935    
32936 });
32937
32938 Roo.apply(Roo.bootstrap.FieldLabel, {
32939     
32940     groups: {},
32941     
32942      /**
32943     * register a FieldLabel Group
32944     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32945     */
32946     register : function(label)
32947     {
32948         if(this.groups.hasOwnProperty(label.target)){
32949             return;
32950         }
32951      
32952         this.groups[label.target] = label;
32953         
32954     },
32955     /**
32956     * fetch a FieldLabel Group based on the target
32957     * @param {string} target
32958     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32959     */
32960     get: function(target) {
32961         if (typeof(this.groups[target]) == 'undefined') {
32962             return false;
32963         }
32964         
32965         return this.groups[target] ;
32966     }
32967 });
32968
32969  
32970
32971  /*
32972  * - LGPL
32973  *
32974  * page DateSplitField.
32975  * 
32976  */
32977
32978
32979 /**
32980  * @class Roo.bootstrap.DateSplitField
32981  * @extends Roo.bootstrap.Component
32982  * Bootstrap DateSplitField class
32983  * @cfg {string} fieldLabel - the label associated
32984  * @cfg {Number} labelWidth set the width of label (0-12)
32985  * @cfg {String} labelAlign (top|left)
32986  * @cfg {Boolean} dayAllowBlank (true|false) default false
32987  * @cfg {Boolean} monthAllowBlank (true|false) default false
32988  * @cfg {Boolean} yearAllowBlank (true|false) default false
32989  * @cfg {string} dayPlaceholder 
32990  * @cfg {string} monthPlaceholder
32991  * @cfg {string} yearPlaceholder
32992  * @cfg {string} dayFormat default 'd'
32993  * @cfg {string} monthFormat default 'm'
32994  * @cfg {string} yearFormat default 'Y'
32995  * @cfg {Number} labellg set the width of label (1-12)
32996  * @cfg {Number} labelmd set the width of label (1-12)
32997  * @cfg {Number} labelsm set the width of label (1-12)
32998  * @cfg {Number} labelxs set the width of label (1-12)
32999
33000  *     
33001  * @constructor
33002  * Create a new DateSplitField
33003  * @param {Object} config The config object
33004  */
33005
33006 Roo.bootstrap.DateSplitField = function(config){
33007     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
33008     
33009     this.addEvents({
33010         // raw events
33011          /**
33012          * @event years
33013          * getting the data of years
33014          * @param {Roo.bootstrap.DateSplitField} this
33015          * @param {Object} years
33016          */
33017         "years" : true,
33018         /**
33019          * @event days
33020          * getting the data of days
33021          * @param {Roo.bootstrap.DateSplitField} this
33022          * @param {Object} days
33023          */
33024         "days" : true,
33025         /**
33026          * @event invalid
33027          * Fires after the field has been marked as invalid.
33028          * @param {Roo.form.Field} this
33029          * @param {String} msg The validation message
33030          */
33031         invalid : true,
33032        /**
33033          * @event valid
33034          * Fires after the field has been validated with no errors.
33035          * @param {Roo.form.Field} this
33036          */
33037         valid : true
33038     });
33039 };
33040
33041 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33042     
33043     fieldLabel : '',
33044     labelAlign : 'top',
33045     labelWidth : 3,
33046     dayAllowBlank : false,
33047     monthAllowBlank : false,
33048     yearAllowBlank : false,
33049     dayPlaceholder : '',
33050     monthPlaceholder : '',
33051     yearPlaceholder : '',
33052     dayFormat : 'd',
33053     monthFormat : 'm',
33054     yearFormat : 'Y',
33055     isFormField : true,
33056     labellg : 0,
33057     labelmd : 0,
33058     labelsm : 0,
33059     labelxs : 0,
33060     
33061     getAutoCreate : function()
33062     {
33063         var cfg = {
33064             tag : 'div',
33065             cls : 'row roo-date-split-field-group',
33066             cn : [
33067                 {
33068                     tag : 'input',
33069                     type : 'hidden',
33070                     cls : 'form-hidden-field roo-date-split-field-group-value',
33071                     name : this.name
33072                 }
33073             ]
33074         };
33075         
33076         var labelCls = 'col-md-12';
33077         var contentCls = 'col-md-4';
33078         
33079         if(this.fieldLabel){
33080             
33081             var label = {
33082                 tag : 'div',
33083                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33084                 cn : [
33085                     {
33086                         tag : 'label',
33087                         html : this.fieldLabel
33088                     }
33089                 ]
33090             };
33091             
33092             if(this.labelAlign == 'left'){
33093             
33094                 if(this.labelWidth > 12){
33095                     label.style = "width: " + this.labelWidth + 'px';
33096                 }
33097
33098                 if(this.labelWidth < 13 && this.labelmd == 0){
33099                     this.labelmd = this.labelWidth;
33100                 }
33101
33102                 if(this.labellg > 0){
33103                     labelCls = ' col-lg-' + this.labellg;
33104                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33105                 }
33106
33107                 if(this.labelmd > 0){
33108                     labelCls = ' col-md-' + this.labelmd;
33109                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33110                 }
33111
33112                 if(this.labelsm > 0){
33113                     labelCls = ' col-sm-' + this.labelsm;
33114                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33115                 }
33116
33117                 if(this.labelxs > 0){
33118                     labelCls = ' col-xs-' + this.labelxs;
33119                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33120                 }
33121             }
33122             
33123             label.cls += ' ' + labelCls;
33124             
33125             cfg.cn.push(label);
33126         }
33127         
33128         Roo.each(['day', 'month', 'year'], function(t){
33129             cfg.cn.push({
33130                 tag : 'div',
33131                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33132             });
33133         }, this);
33134         
33135         return cfg;
33136     },
33137     
33138     inputEl: function ()
33139     {
33140         return this.el.select('.roo-date-split-field-group-value', true).first();
33141     },
33142     
33143     onRender : function(ct, position) 
33144     {
33145         var _this = this;
33146         
33147         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33148         
33149         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33150         
33151         this.dayField = new Roo.bootstrap.ComboBox({
33152             allowBlank : this.dayAllowBlank,
33153             alwaysQuery : true,
33154             displayField : 'value',
33155             editable : false,
33156             fieldLabel : '',
33157             forceSelection : true,
33158             mode : 'local',
33159             placeholder : this.dayPlaceholder,
33160             selectOnFocus : true,
33161             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33162             triggerAction : 'all',
33163             typeAhead : true,
33164             valueField : 'value',
33165             store : new Roo.data.SimpleStore({
33166                 data : (function() {    
33167                     var days = [];
33168                     _this.fireEvent('days', _this, days);
33169                     return days;
33170                 })(),
33171                 fields : [ 'value' ]
33172             }),
33173             listeners : {
33174                 select : function (_self, record, index)
33175                 {
33176                     _this.setValue(_this.getValue());
33177                 }
33178             }
33179         });
33180
33181         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33182         
33183         this.monthField = new Roo.bootstrap.MonthField({
33184             after : '<i class=\"fa fa-calendar\"></i>',
33185             allowBlank : this.monthAllowBlank,
33186             placeholder : this.monthPlaceholder,
33187             readOnly : true,
33188             listeners : {
33189                 render : function (_self)
33190                 {
33191                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33192                         e.preventDefault();
33193                         _self.focus();
33194                     });
33195                 },
33196                 select : function (_self, oldvalue, newvalue)
33197                 {
33198                     _this.setValue(_this.getValue());
33199                 }
33200             }
33201         });
33202         
33203         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33204         
33205         this.yearField = new Roo.bootstrap.ComboBox({
33206             allowBlank : this.yearAllowBlank,
33207             alwaysQuery : true,
33208             displayField : 'value',
33209             editable : false,
33210             fieldLabel : '',
33211             forceSelection : true,
33212             mode : 'local',
33213             placeholder : this.yearPlaceholder,
33214             selectOnFocus : true,
33215             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33216             triggerAction : 'all',
33217             typeAhead : true,
33218             valueField : 'value',
33219             store : new Roo.data.SimpleStore({
33220                 data : (function() {
33221                     var years = [];
33222                     _this.fireEvent('years', _this, years);
33223                     return years;
33224                 })(),
33225                 fields : [ 'value' ]
33226             }),
33227             listeners : {
33228                 select : function (_self, record, index)
33229                 {
33230                     _this.setValue(_this.getValue());
33231                 }
33232             }
33233         });
33234
33235         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33236     },
33237     
33238     setValue : function(v, format)
33239     {
33240         this.inputEl.dom.value = v;
33241         
33242         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33243         
33244         var d = Date.parseDate(v, f);
33245         
33246         if(!d){
33247             this.validate();
33248             return;
33249         }
33250         
33251         this.setDay(d.format(this.dayFormat));
33252         this.setMonth(d.format(this.monthFormat));
33253         this.setYear(d.format(this.yearFormat));
33254         
33255         this.validate();
33256         
33257         return;
33258     },
33259     
33260     setDay : function(v)
33261     {
33262         this.dayField.setValue(v);
33263         this.inputEl.dom.value = this.getValue();
33264         this.validate();
33265         return;
33266     },
33267     
33268     setMonth : function(v)
33269     {
33270         this.monthField.setValue(v, true);
33271         this.inputEl.dom.value = this.getValue();
33272         this.validate();
33273         return;
33274     },
33275     
33276     setYear : function(v)
33277     {
33278         this.yearField.setValue(v);
33279         this.inputEl.dom.value = this.getValue();
33280         this.validate();
33281         return;
33282     },
33283     
33284     getDay : function()
33285     {
33286         return this.dayField.getValue();
33287     },
33288     
33289     getMonth : function()
33290     {
33291         return this.monthField.getValue();
33292     },
33293     
33294     getYear : function()
33295     {
33296         return this.yearField.getValue();
33297     },
33298     
33299     getValue : function()
33300     {
33301         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33302         
33303         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33304         
33305         return date;
33306     },
33307     
33308     reset : function()
33309     {
33310         this.setDay('');
33311         this.setMonth('');
33312         this.setYear('');
33313         this.inputEl.dom.value = '';
33314         this.validate();
33315         return;
33316     },
33317     
33318     validate : function()
33319     {
33320         var d = this.dayField.validate();
33321         var m = this.monthField.validate();
33322         var y = this.yearField.validate();
33323         
33324         var valid = true;
33325         
33326         if(
33327                 (!this.dayAllowBlank && !d) ||
33328                 (!this.monthAllowBlank && !m) ||
33329                 (!this.yearAllowBlank && !y)
33330         ){
33331             valid = false;
33332         }
33333         
33334         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33335             return valid;
33336         }
33337         
33338         if(valid){
33339             this.markValid();
33340             return valid;
33341         }
33342         
33343         this.markInvalid();
33344         
33345         return valid;
33346     },
33347     
33348     markValid : function()
33349     {
33350         
33351         var label = this.el.select('label', true).first();
33352         var icon = this.el.select('i.fa-star', true).first();
33353
33354         if(label && icon){
33355             icon.remove();
33356         }
33357         
33358         this.fireEvent('valid', this);
33359     },
33360     
33361      /**
33362      * Mark this field as invalid
33363      * @param {String} msg The validation message
33364      */
33365     markInvalid : function(msg)
33366     {
33367         
33368         var label = this.el.select('label', true).first();
33369         var icon = this.el.select('i.fa-star', true).first();
33370
33371         if(label && !icon){
33372             this.el.select('.roo-date-split-field-label', true).createChild({
33373                 tag : 'i',
33374                 cls : 'text-danger fa fa-lg fa-star',
33375                 tooltip : 'This field is required',
33376                 style : 'margin-right:5px;'
33377             }, label, true);
33378         }
33379         
33380         this.fireEvent('invalid', this, msg);
33381     },
33382     
33383     clearInvalid : function()
33384     {
33385         var label = this.el.select('label', true).first();
33386         var icon = this.el.select('i.fa-star', true).first();
33387
33388         if(label && icon){
33389             icon.remove();
33390         }
33391         
33392         this.fireEvent('valid', this);
33393     },
33394     
33395     getName: function()
33396     {
33397         return this.name;
33398     }
33399     
33400 });
33401
33402  /**
33403  *
33404  * This is based on 
33405  * http://masonry.desandro.com
33406  *
33407  * The idea is to render all the bricks based on vertical width...
33408  *
33409  * The original code extends 'outlayer' - we might need to use that....
33410  * 
33411  */
33412
33413
33414 /**
33415  * @class Roo.bootstrap.LayoutMasonry
33416  * @extends Roo.bootstrap.Component
33417  * Bootstrap Layout Masonry class
33418  * 
33419  * @constructor
33420  * Create a new Element
33421  * @param {Object} config The config object
33422  */
33423
33424 Roo.bootstrap.LayoutMasonry = function(config){
33425     
33426     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33427     
33428     this.bricks = [];
33429     
33430     Roo.bootstrap.LayoutMasonry.register(this);
33431     
33432     this.addEvents({
33433         // raw events
33434         /**
33435          * @event layout
33436          * Fire after layout the items
33437          * @param {Roo.bootstrap.LayoutMasonry} this
33438          * @param {Roo.EventObject} e
33439          */
33440         "layout" : true
33441     });
33442     
33443 };
33444
33445 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33446     
33447     /**
33448      * @cfg {Boolean} isLayoutInstant = no animation?
33449      */   
33450     isLayoutInstant : false, // needed?
33451    
33452     /**
33453      * @cfg {Number} boxWidth  width of the columns
33454      */   
33455     boxWidth : 450,
33456     
33457       /**
33458      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33459      */   
33460     boxHeight : 0,
33461     
33462     /**
33463      * @cfg {Number} padWidth padding below box..
33464      */   
33465     padWidth : 10, 
33466     
33467     /**
33468      * @cfg {Number} gutter gutter width..
33469      */   
33470     gutter : 10,
33471     
33472      /**
33473      * @cfg {Number} maxCols maximum number of columns
33474      */   
33475     
33476     maxCols: 0,
33477     
33478     /**
33479      * @cfg {Boolean} isAutoInitial defalut true
33480      */   
33481     isAutoInitial : true, 
33482     
33483     containerWidth: 0,
33484     
33485     /**
33486      * @cfg {Boolean} isHorizontal defalut false
33487      */   
33488     isHorizontal : false, 
33489
33490     currentSize : null,
33491     
33492     tag: 'div',
33493     
33494     cls: '',
33495     
33496     bricks: null, //CompositeElement
33497     
33498     cols : 1,
33499     
33500     _isLayoutInited : false,
33501     
33502 //    isAlternative : false, // only use for vertical layout...
33503     
33504     /**
33505      * @cfg {Number} alternativePadWidth padding below box..
33506      */   
33507     alternativePadWidth : 50,
33508     
33509     selectedBrick : [],
33510     
33511     getAutoCreate : function(){
33512         
33513         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33514         
33515         var cfg = {
33516             tag: this.tag,
33517             cls: 'blog-masonary-wrapper ' + this.cls,
33518             cn : {
33519                 cls : 'mas-boxes masonary'
33520             }
33521         };
33522         
33523         return cfg;
33524     },
33525     
33526     getChildContainer: function( )
33527     {
33528         if (this.boxesEl) {
33529             return this.boxesEl;
33530         }
33531         
33532         this.boxesEl = this.el.select('.mas-boxes').first();
33533         
33534         return this.boxesEl;
33535     },
33536     
33537     
33538     initEvents : function()
33539     {
33540         var _this = this;
33541         
33542         if(this.isAutoInitial){
33543             Roo.log('hook children rendered');
33544             this.on('childrenrendered', function() {
33545                 Roo.log('children rendered');
33546                 _this.initial();
33547             } ,this);
33548         }
33549     },
33550     
33551     initial : function()
33552     {
33553         this.selectedBrick = [];
33554         
33555         this.currentSize = this.el.getBox(true);
33556         
33557         Roo.EventManager.onWindowResize(this.resize, this); 
33558
33559         if(!this.isAutoInitial){
33560             this.layout();
33561             return;
33562         }
33563         
33564         this.layout();
33565         
33566         return;
33567         //this.layout.defer(500,this);
33568         
33569     },
33570     
33571     resize : function()
33572     {
33573         var cs = this.el.getBox(true);
33574         
33575         if (
33576                 this.currentSize.width == cs.width && 
33577                 this.currentSize.x == cs.x && 
33578                 this.currentSize.height == cs.height && 
33579                 this.currentSize.y == cs.y 
33580         ) {
33581             Roo.log("no change in with or X or Y");
33582             return;
33583         }
33584         
33585         this.currentSize = cs;
33586         
33587         this.layout();
33588         
33589     },
33590     
33591     layout : function()
33592     {   
33593         this._resetLayout();
33594         
33595         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33596         
33597         this.layoutItems( isInstant );
33598       
33599         this._isLayoutInited = true;
33600         
33601         this.fireEvent('layout', this);
33602         
33603     },
33604     
33605     _resetLayout : function()
33606     {
33607         if(this.isHorizontal){
33608             this.horizontalMeasureColumns();
33609             return;
33610         }
33611         
33612         this.verticalMeasureColumns();
33613         
33614     },
33615     
33616     verticalMeasureColumns : function()
33617     {
33618         this.getContainerWidth();
33619         
33620 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33621 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33622 //            return;
33623 //        }
33624         
33625         var boxWidth = this.boxWidth + this.padWidth;
33626         
33627         if(this.containerWidth < this.boxWidth){
33628             boxWidth = this.containerWidth
33629         }
33630         
33631         var containerWidth = this.containerWidth;
33632         
33633         var cols = Math.floor(containerWidth / boxWidth);
33634         
33635         this.cols = Math.max( cols, 1 );
33636         
33637         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33638         
33639         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33640         
33641         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33642         
33643         this.colWidth = boxWidth + avail - this.padWidth;
33644         
33645         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33646         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33647     },
33648     
33649     horizontalMeasureColumns : function()
33650     {
33651         this.getContainerWidth();
33652         
33653         var boxWidth = this.boxWidth;
33654         
33655         if(this.containerWidth < boxWidth){
33656             boxWidth = this.containerWidth;
33657         }
33658         
33659         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33660         
33661         this.el.setHeight(boxWidth);
33662         
33663     },
33664     
33665     getContainerWidth : function()
33666     {
33667         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33668     },
33669     
33670     layoutItems : function( isInstant )
33671     {
33672         Roo.log(this.bricks);
33673         
33674         var items = Roo.apply([], this.bricks);
33675         
33676         if(this.isHorizontal){
33677             this._horizontalLayoutItems( items , isInstant );
33678             return;
33679         }
33680         
33681 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33682 //            this._verticalAlternativeLayoutItems( items , isInstant );
33683 //            return;
33684 //        }
33685         
33686         this._verticalLayoutItems( items , isInstant );
33687         
33688     },
33689     
33690     _verticalLayoutItems : function ( items , isInstant)
33691     {
33692         if ( !items || !items.length ) {
33693             return;
33694         }
33695         
33696         var standard = [
33697             ['xs', 'xs', 'xs', 'tall'],
33698             ['xs', 'xs', 'tall'],
33699             ['xs', 'xs', 'sm'],
33700             ['xs', 'xs', 'xs'],
33701             ['xs', 'tall'],
33702             ['xs', 'sm'],
33703             ['xs', 'xs'],
33704             ['xs'],
33705             
33706             ['sm', 'xs', 'xs'],
33707             ['sm', 'xs'],
33708             ['sm'],
33709             
33710             ['tall', 'xs', 'xs', 'xs'],
33711             ['tall', 'xs', 'xs'],
33712             ['tall', 'xs'],
33713             ['tall']
33714             
33715         ];
33716         
33717         var queue = [];
33718         
33719         var boxes = [];
33720         
33721         var box = [];
33722         
33723         Roo.each(items, function(item, k){
33724             
33725             switch (item.size) {
33726                 // these layouts take up a full box,
33727                 case 'md' :
33728                 case 'md-left' :
33729                 case 'md-right' :
33730                 case 'wide' :
33731                     
33732                     if(box.length){
33733                         boxes.push(box);
33734                         box = [];
33735                     }
33736                     
33737                     boxes.push([item]);
33738                     
33739                     break;
33740                     
33741                 case 'xs' :
33742                 case 'sm' :
33743                 case 'tall' :
33744                     
33745                     box.push(item);
33746                     
33747                     break;
33748                 default :
33749                     break;
33750                     
33751             }
33752             
33753         }, this);
33754         
33755         if(box.length){
33756             boxes.push(box);
33757             box = [];
33758         }
33759         
33760         var filterPattern = function(box, length)
33761         {
33762             if(!box.length){
33763                 return;
33764             }
33765             
33766             var match = false;
33767             
33768             var pattern = box.slice(0, length);
33769             
33770             var format = [];
33771             
33772             Roo.each(pattern, function(i){
33773                 format.push(i.size);
33774             }, this);
33775             
33776             Roo.each(standard, function(s){
33777                 
33778                 if(String(s) != String(format)){
33779                     return;
33780                 }
33781                 
33782                 match = true;
33783                 return false;
33784                 
33785             }, this);
33786             
33787             if(!match && length == 1){
33788                 return;
33789             }
33790             
33791             if(!match){
33792                 filterPattern(box, length - 1);
33793                 return;
33794             }
33795                 
33796             queue.push(pattern);
33797
33798             box = box.slice(length, box.length);
33799
33800             filterPattern(box, 4);
33801
33802             return;
33803             
33804         }
33805         
33806         Roo.each(boxes, function(box, k){
33807             
33808             if(!box.length){
33809                 return;
33810             }
33811             
33812             if(box.length == 1){
33813                 queue.push(box);
33814                 return;
33815             }
33816             
33817             filterPattern(box, 4);
33818             
33819         }, this);
33820         
33821         this._processVerticalLayoutQueue( queue, isInstant );
33822         
33823     },
33824     
33825 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33826 //    {
33827 //        if ( !items || !items.length ) {
33828 //            return;
33829 //        }
33830 //
33831 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33832 //        
33833 //    },
33834     
33835     _horizontalLayoutItems : function ( items , isInstant)
33836     {
33837         if ( !items || !items.length || items.length < 3) {
33838             return;
33839         }
33840         
33841         items.reverse();
33842         
33843         var eItems = items.slice(0, 3);
33844         
33845         items = items.slice(3, items.length);
33846         
33847         var standard = [
33848             ['xs', 'xs', 'xs', 'wide'],
33849             ['xs', 'xs', 'wide'],
33850             ['xs', 'xs', 'sm'],
33851             ['xs', 'xs', 'xs'],
33852             ['xs', 'wide'],
33853             ['xs', 'sm'],
33854             ['xs', 'xs'],
33855             ['xs'],
33856             
33857             ['sm', 'xs', 'xs'],
33858             ['sm', 'xs'],
33859             ['sm'],
33860             
33861             ['wide', 'xs', 'xs', 'xs'],
33862             ['wide', 'xs', 'xs'],
33863             ['wide', 'xs'],
33864             ['wide'],
33865             
33866             ['wide-thin']
33867         ];
33868         
33869         var queue = [];
33870         
33871         var boxes = [];
33872         
33873         var box = [];
33874         
33875         Roo.each(items, function(item, k){
33876             
33877             switch (item.size) {
33878                 case 'md' :
33879                 case 'md-left' :
33880                 case 'md-right' :
33881                 case 'tall' :
33882                     
33883                     if(box.length){
33884                         boxes.push(box);
33885                         box = [];
33886                     }
33887                     
33888                     boxes.push([item]);
33889                     
33890                     break;
33891                     
33892                 case 'xs' :
33893                 case 'sm' :
33894                 case 'wide' :
33895                 case 'wide-thin' :
33896                     
33897                     box.push(item);
33898                     
33899                     break;
33900                 default :
33901                     break;
33902                     
33903             }
33904             
33905         }, this);
33906         
33907         if(box.length){
33908             boxes.push(box);
33909             box = [];
33910         }
33911         
33912         var filterPattern = function(box, length)
33913         {
33914             if(!box.length){
33915                 return;
33916             }
33917             
33918             var match = false;
33919             
33920             var pattern = box.slice(0, length);
33921             
33922             var format = [];
33923             
33924             Roo.each(pattern, function(i){
33925                 format.push(i.size);
33926             }, this);
33927             
33928             Roo.each(standard, function(s){
33929                 
33930                 if(String(s) != String(format)){
33931                     return;
33932                 }
33933                 
33934                 match = true;
33935                 return false;
33936                 
33937             }, this);
33938             
33939             if(!match && length == 1){
33940                 return;
33941             }
33942             
33943             if(!match){
33944                 filterPattern(box, length - 1);
33945                 return;
33946             }
33947                 
33948             queue.push(pattern);
33949
33950             box = box.slice(length, box.length);
33951
33952             filterPattern(box, 4);
33953
33954             return;
33955             
33956         }
33957         
33958         Roo.each(boxes, function(box, k){
33959             
33960             if(!box.length){
33961                 return;
33962             }
33963             
33964             if(box.length == 1){
33965                 queue.push(box);
33966                 return;
33967             }
33968             
33969             filterPattern(box, 4);
33970             
33971         }, this);
33972         
33973         
33974         var prune = [];
33975         
33976         var pos = this.el.getBox(true);
33977         
33978         var minX = pos.x;
33979         
33980         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33981         
33982         var hit_end = false;
33983         
33984         Roo.each(queue, function(box){
33985             
33986             if(hit_end){
33987                 
33988                 Roo.each(box, function(b){
33989                 
33990                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33991                     b.el.hide();
33992
33993                 }, this);
33994
33995                 return;
33996             }
33997             
33998             var mx = 0;
33999             
34000             Roo.each(box, function(b){
34001                 
34002                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
34003                 b.el.show();
34004
34005                 mx = Math.max(mx, b.x);
34006                 
34007             }, this);
34008             
34009             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
34010             
34011             if(maxX < minX){
34012                 
34013                 Roo.each(box, function(b){
34014                 
34015                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
34016                     b.el.hide();
34017                     
34018                 }, this);
34019                 
34020                 hit_end = true;
34021                 
34022                 return;
34023             }
34024             
34025             prune.push(box);
34026             
34027         }, this);
34028         
34029         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34030     },
34031     
34032     /** Sets position of item in DOM
34033     * @param {Element} item
34034     * @param {Number} x - horizontal position
34035     * @param {Number} y - vertical position
34036     * @param {Boolean} isInstant - disables transitions
34037     */
34038     _processVerticalLayoutQueue : function( queue, isInstant )
34039     {
34040         var pos = this.el.getBox(true);
34041         var x = pos.x;
34042         var y = pos.y;
34043         var maxY = [];
34044         
34045         for (var i = 0; i < this.cols; i++){
34046             maxY[i] = pos.y;
34047         }
34048         
34049         Roo.each(queue, function(box, k){
34050             
34051             var col = k % this.cols;
34052             
34053             Roo.each(box, function(b,kk){
34054                 
34055                 b.el.position('absolute');
34056                 
34057                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34058                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34059                 
34060                 if(b.size == 'md-left' || b.size == 'md-right'){
34061                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34062                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34063                 }
34064                 
34065                 b.el.setWidth(width);
34066                 b.el.setHeight(height);
34067                 // iframe?
34068                 b.el.select('iframe',true).setSize(width,height);
34069                 
34070             }, this);
34071             
34072             for (var i = 0; i < this.cols; i++){
34073                 
34074                 if(maxY[i] < maxY[col]){
34075                     col = i;
34076                     continue;
34077                 }
34078                 
34079                 col = Math.min(col, i);
34080                 
34081             }
34082             
34083             x = pos.x + col * (this.colWidth + this.padWidth);
34084             
34085             y = maxY[col];
34086             
34087             var positions = [];
34088             
34089             switch (box.length){
34090                 case 1 :
34091                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34092                     break;
34093                 case 2 :
34094                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34095                     break;
34096                 case 3 :
34097                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34098                     break;
34099                 case 4 :
34100                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34101                     break;
34102                 default :
34103                     break;
34104             }
34105             
34106             Roo.each(box, function(b,kk){
34107                 
34108                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34109                 
34110                 var sz = b.el.getSize();
34111                 
34112                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34113                 
34114             }, this);
34115             
34116         }, this);
34117         
34118         var mY = 0;
34119         
34120         for (var i = 0; i < this.cols; i++){
34121             mY = Math.max(mY, maxY[i]);
34122         }
34123         
34124         this.el.setHeight(mY - pos.y);
34125         
34126     },
34127     
34128 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34129 //    {
34130 //        var pos = this.el.getBox(true);
34131 //        var x = pos.x;
34132 //        var y = pos.y;
34133 //        var maxX = pos.right;
34134 //        
34135 //        var maxHeight = 0;
34136 //        
34137 //        Roo.each(items, function(item, k){
34138 //            
34139 //            var c = k % 2;
34140 //            
34141 //            item.el.position('absolute');
34142 //                
34143 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34144 //
34145 //            item.el.setWidth(width);
34146 //
34147 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34148 //
34149 //            item.el.setHeight(height);
34150 //            
34151 //            if(c == 0){
34152 //                item.el.setXY([x, y], isInstant ? false : true);
34153 //            } else {
34154 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34155 //            }
34156 //            
34157 //            y = y + height + this.alternativePadWidth;
34158 //            
34159 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34160 //            
34161 //        }, this);
34162 //        
34163 //        this.el.setHeight(maxHeight);
34164 //        
34165 //    },
34166     
34167     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34168     {
34169         var pos = this.el.getBox(true);
34170         
34171         var minX = pos.x;
34172         var minY = pos.y;
34173         
34174         var maxX = pos.right;
34175         
34176         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34177         
34178         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34179         
34180         Roo.each(queue, function(box, k){
34181             
34182             Roo.each(box, function(b, kk){
34183                 
34184                 b.el.position('absolute');
34185                 
34186                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34187                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34188                 
34189                 if(b.size == 'md-left' || b.size == 'md-right'){
34190                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34191                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34192                 }
34193                 
34194                 b.el.setWidth(width);
34195                 b.el.setHeight(height);
34196                 
34197             }, this);
34198             
34199             if(!box.length){
34200                 return;
34201             }
34202             
34203             var positions = [];
34204             
34205             switch (box.length){
34206                 case 1 :
34207                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34208                     break;
34209                 case 2 :
34210                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34211                     break;
34212                 case 3 :
34213                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34214                     break;
34215                 case 4 :
34216                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34217                     break;
34218                 default :
34219                     break;
34220             }
34221             
34222             Roo.each(box, function(b,kk){
34223                 
34224                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34225                 
34226                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34227                 
34228             }, this);
34229             
34230         }, this);
34231         
34232     },
34233     
34234     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34235     {
34236         Roo.each(eItems, function(b,k){
34237             
34238             b.size = (k == 0) ? 'sm' : 'xs';
34239             b.x = (k == 0) ? 2 : 1;
34240             b.y = (k == 0) ? 2 : 1;
34241             
34242             b.el.position('absolute');
34243             
34244             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34245                 
34246             b.el.setWidth(width);
34247             
34248             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34249             
34250             b.el.setHeight(height);
34251             
34252         }, this);
34253
34254         var positions = [];
34255         
34256         positions.push({
34257             x : maxX - this.unitWidth * 2 - this.gutter,
34258             y : minY
34259         });
34260         
34261         positions.push({
34262             x : maxX - this.unitWidth,
34263             y : minY + (this.unitWidth + this.gutter) * 2
34264         });
34265         
34266         positions.push({
34267             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34268             y : minY
34269         });
34270         
34271         Roo.each(eItems, function(b,k){
34272             
34273             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34274
34275         }, this);
34276         
34277     },
34278     
34279     getVerticalOneBoxColPositions : function(x, y, box)
34280     {
34281         var pos = [];
34282         
34283         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34284         
34285         if(box[0].size == 'md-left'){
34286             rand = 0;
34287         }
34288         
34289         if(box[0].size == 'md-right'){
34290             rand = 1;
34291         }
34292         
34293         pos.push({
34294             x : x + (this.unitWidth + this.gutter) * rand,
34295             y : y
34296         });
34297         
34298         return pos;
34299     },
34300     
34301     getVerticalTwoBoxColPositions : function(x, y, box)
34302     {
34303         var pos = [];
34304         
34305         if(box[0].size == 'xs'){
34306             
34307             pos.push({
34308                 x : x,
34309                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34310             });
34311
34312             pos.push({
34313                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34314                 y : y
34315             });
34316             
34317             return pos;
34318             
34319         }
34320         
34321         pos.push({
34322             x : x,
34323             y : y
34324         });
34325
34326         pos.push({
34327             x : x + (this.unitWidth + this.gutter) * 2,
34328             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34329         });
34330         
34331         return pos;
34332         
34333     },
34334     
34335     getVerticalThreeBoxColPositions : function(x, y, box)
34336     {
34337         var pos = [];
34338         
34339         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34340             
34341             pos.push({
34342                 x : x,
34343                 y : y
34344             });
34345
34346             pos.push({
34347                 x : x + (this.unitWidth + this.gutter) * 1,
34348                 y : y
34349             });
34350             
34351             pos.push({
34352                 x : x + (this.unitWidth + this.gutter) * 2,
34353                 y : y
34354             });
34355             
34356             return pos;
34357             
34358         }
34359         
34360         if(box[0].size == 'xs' && box[1].size == 'xs'){
34361             
34362             pos.push({
34363                 x : x,
34364                 y : y
34365             });
34366
34367             pos.push({
34368                 x : x,
34369                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34370             });
34371             
34372             pos.push({
34373                 x : x + (this.unitWidth + this.gutter) * 1,
34374                 y : y
34375             });
34376             
34377             return pos;
34378             
34379         }
34380         
34381         pos.push({
34382             x : x,
34383             y : y
34384         });
34385
34386         pos.push({
34387             x : x + (this.unitWidth + this.gutter) * 2,
34388             y : y
34389         });
34390
34391         pos.push({
34392             x : x + (this.unitWidth + this.gutter) * 2,
34393             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34394         });
34395             
34396         return pos;
34397         
34398     },
34399     
34400     getVerticalFourBoxColPositions : function(x, y, box)
34401     {
34402         var pos = [];
34403         
34404         if(box[0].size == 'xs'){
34405             
34406             pos.push({
34407                 x : x,
34408                 y : y
34409             });
34410
34411             pos.push({
34412                 x : x,
34413                 y : y + (this.unitHeight + this.gutter) * 1
34414             });
34415             
34416             pos.push({
34417                 x : x,
34418                 y : y + (this.unitHeight + this.gutter) * 2
34419             });
34420             
34421             pos.push({
34422                 x : x + (this.unitWidth + this.gutter) * 1,
34423                 y : y
34424             });
34425             
34426             return pos;
34427             
34428         }
34429         
34430         pos.push({
34431             x : x,
34432             y : y
34433         });
34434
34435         pos.push({
34436             x : x + (this.unitWidth + this.gutter) * 2,
34437             y : y
34438         });
34439
34440         pos.push({
34441             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34442             y : y + (this.unitHeight + this.gutter) * 1
34443         });
34444
34445         pos.push({
34446             x : x + (this.unitWidth + this.gutter) * 2,
34447             y : y + (this.unitWidth + this.gutter) * 2
34448         });
34449
34450         return pos;
34451         
34452     },
34453     
34454     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34455     {
34456         var pos = [];
34457         
34458         if(box[0].size == 'md-left'){
34459             pos.push({
34460                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34461                 y : minY
34462             });
34463             
34464             return pos;
34465         }
34466         
34467         if(box[0].size == 'md-right'){
34468             pos.push({
34469                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34470                 y : minY + (this.unitWidth + this.gutter) * 1
34471             });
34472             
34473             return pos;
34474         }
34475         
34476         var rand = Math.floor(Math.random() * (4 - box[0].y));
34477         
34478         pos.push({
34479             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34480             y : minY + (this.unitWidth + this.gutter) * rand
34481         });
34482         
34483         return pos;
34484         
34485     },
34486     
34487     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34488     {
34489         var pos = [];
34490         
34491         if(box[0].size == 'xs'){
34492             
34493             pos.push({
34494                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34495                 y : minY
34496             });
34497
34498             pos.push({
34499                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34500                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34501             });
34502             
34503             return pos;
34504             
34505         }
34506         
34507         pos.push({
34508             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34509             y : minY
34510         });
34511
34512         pos.push({
34513             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34514             y : minY + (this.unitWidth + this.gutter) * 2
34515         });
34516         
34517         return pos;
34518         
34519     },
34520     
34521     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34522     {
34523         var pos = [];
34524         
34525         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34526             
34527             pos.push({
34528                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34529                 y : minY
34530             });
34531
34532             pos.push({
34533                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34534                 y : minY + (this.unitWidth + this.gutter) * 1
34535             });
34536             
34537             pos.push({
34538                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34539                 y : minY + (this.unitWidth + this.gutter) * 2
34540             });
34541             
34542             return pos;
34543             
34544         }
34545         
34546         if(box[0].size == 'xs' && box[1].size == 'xs'){
34547             
34548             pos.push({
34549                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34550                 y : minY
34551             });
34552
34553             pos.push({
34554                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34555                 y : minY
34556             });
34557             
34558             pos.push({
34559                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34560                 y : minY + (this.unitWidth + this.gutter) * 1
34561             });
34562             
34563             return pos;
34564             
34565         }
34566         
34567         pos.push({
34568             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34569             y : minY
34570         });
34571
34572         pos.push({
34573             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34574             y : minY + (this.unitWidth + this.gutter) * 2
34575         });
34576
34577         pos.push({
34578             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34579             y : minY + (this.unitWidth + this.gutter) * 2
34580         });
34581             
34582         return pos;
34583         
34584     },
34585     
34586     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34587     {
34588         var pos = [];
34589         
34590         if(box[0].size == 'xs'){
34591             
34592             pos.push({
34593                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34594                 y : minY
34595             });
34596
34597             pos.push({
34598                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34599                 y : minY
34600             });
34601             
34602             pos.push({
34603                 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),
34604                 y : minY
34605             });
34606             
34607             pos.push({
34608                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34609                 y : minY + (this.unitWidth + this.gutter) * 1
34610             });
34611             
34612             return pos;
34613             
34614         }
34615         
34616         pos.push({
34617             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34618             y : minY
34619         });
34620         
34621         pos.push({
34622             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34623             y : minY + (this.unitWidth + this.gutter) * 2
34624         });
34625         
34626         pos.push({
34627             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34628             y : minY + (this.unitWidth + this.gutter) * 2
34629         });
34630         
34631         pos.push({
34632             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),
34633             y : minY + (this.unitWidth + this.gutter) * 2
34634         });
34635
34636         return pos;
34637         
34638     },
34639     
34640     /**
34641     * remove a Masonry Brick
34642     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34643     */
34644     removeBrick : function(brick_id)
34645     {
34646         if (!brick_id) {
34647             return;
34648         }
34649         
34650         for (var i = 0; i<this.bricks.length; i++) {
34651             if (this.bricks[i].id == brick_id) {
34652                 this.bricks.splice(i,1);
34653                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34654                 this.initial();
34655             }
34656         }
34657     },
34658     
34659     /**
34660     * adds a Masonry Brick
34661     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34662     */
34663     addBrick : function(cfg)
34664     {
34665         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34666         //this.register(cn);
34667         cn.parentId = this.id;
34668         cn.render(this.el);
34669         return cn;
34670     },
34671     
34672     /**
34673     * register a Masonry Brick
34674     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34675     */
34676     
34677     register : function(brick)
34678     {
34679         this.bricks.push(brick);
34680         brick.masonryId = this.id;
34681     },
34682     
34683     /**
34684     * clear all the Masonry Brick
34685     */
34686     clearAll : function()
34687     {
34688         this.bricks = [];
34689         //this.getChildContainer().dom.innerHTML = "";
34690         this.el.dom.innerHTML = '';
34691     },
34692     
34693     getSelected : function()
34694     {
34695         if (!this.selectedBrick) {
34696             return false;
34697         }
34698         
34699         return this.selectedBrick;
34700     }
34701 });
34702
34703 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34704     
34705     groups: {},
34706      /**
34707     * register a Masonry Layout
34708     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34709     */
34710     
34711     register : function(layout)
34712     {
34713         this.groups[layout.id] = layout;
34714     },
34715     /**
34716     * fetch a  Masonry Layout based on the masonry layout ID
34717     * @param {string} the masonry layout to add
34718     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34719     */
34720     
34721     get: function(layout_id) {
34722         if (typeof(this.groups[layout_id]) == 'undefined') {
34723             return false;
34724         }
34725         return this.groups[layout_id] ;
34726     }
34727     
34728     
34729     
34730 });
34731
34732  
34733
34734  /**
34735  *
34736  * This is based on 
34737  * http://masonry.desandro.com
34738  *
34739  * The idea is to render all the bricks based on vertical width...
34740  *
34741  * The original code extends 'outlayer' - we might need to use that....
34742  * 
34743  */
34744
34745
34746 /**
34747  * @class Roo.bootstrap.LayoutMasonryAuto
34748  * @extends Roo.bootstrap.Component
34749  * Bootstrap Layout Masonry class
34750  * 
34751  * @constructor
34752  * Create a new Element
34753  * @param {Object} config The config object
34754  */
34755
34756 Roo.bootstrap.LayoutMasonryAuto = function(config){
34757     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34758 };
34759
34760 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34761     
34762       /**
34763      * @cfg {Boolean} isFitWidth  - resize the width..
34764      */   
34765     isFitWidth : false,  // options..
34766     /**
34767      * @cfg {Boolean} isOriginLeft = left align?
34768      */   
34769     isOriginLeft : true,
34770     /**
34771      * @cfg {Boolean} isOriginTop = top align?
34772      */   
34773     isOriginTop : false,
34774     /**
34775      * @cfg {Boolean} isLayoutInstant = no animation?
34776      */   
34777     isLayoutInstant : false, // needed?
34778     /**
34779      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34780      */   
34781     isResizingContainer : true,
34782     /**
34783      * @cfg {Number} columnWidth  width of the columns 
34784      */   
34785     
34786     columnWidth : 0,
34787     
34788     /**
34789      * @cfg {Number} maxCols maximum number of columns
34790      */   
34791     
34792     maxCols: 0,
34793     /**
34794      * @cfg {Number} padHeight padding below box..
34795      */   
34796     
34797     padHeight : 10, 
34798     
34799     /**
34800      * @cfg {Boolean} isAutoInitial defalut true
34801      */   
34802     
34803     isAutoInitial : true, 
34804     
34805     // private?
34806     gutter : 0,
34807     
34808     containerWidth: 0,
34809     initialColumnWidth : 0,
34810     currentSize : null,
34811     
34812     colYs : null, // array.
34813     maxY : 0,
34814     padWidth: 10,
34815     
34816     
34817     tag: 'div',
34818     cls: '',
34819     bricks: null, //CompositeElement
34820     cols : 0, // array?
34821     // element : null, // wrapped now this.el
34822     _isLayoutInited : null, 
34823     
34824     
34825     getAutoCreate : function(){
34826         
34827         var cfg = {
34828             tag: this.tag,
34829             cls: 'blog-masonary-wrapper ' + this.cls,
34830             cn : {
34831                 cls : 'mas-boxes masonary'
34832             }
34833         };
34834         
34835         return cfg;
34836     },
34837     
34838     getChildContainer: function( )
34839     {
34840         if (this.boxesEl) {
34841             return this.boxesEl;
34842         }
34843         
34844         this.boxesEl = this.el.select('.mas-boxes').first();
34845         
34846         return this.boxesEl;
34847     },
34848     
34849     
34850     initEvents : function()
34851     {
34852         var _this = this;
34853         
34854         if(this.isAutoInitial){
34855             Roo.log('hook children rendered');
34856             this.on('childrenrendered', function() {
34857                 Roo.log('children rendered');
34858                 _this.initial();
34859             } ,this);
34860         }
34861         
34862     },
34863     
34864     initial : function()
34865     {
34866         this.reloadItems();
34867
34868         this.currentSize = this.el.getBox(true);
34869
34870         /// was window resize... - let's see if this works..
34871         Roo.EventManager.onWindowResize(this.resize, this); 
34872
34873         if(!this.isAutoInitial){
34874             this.layout();
34875             return;
34876         }
34877         
34878         this.layout.defer(500,this);
34879     },
34880     
34881     reloadItems: function()
34882     {
34883         this.bricks = this.el.select('.masonry-brick', true);
34884         
34885         this.bricks.each(function(b) {
34886             //Roo.log(b.getSize());
34887             if (!b.attr('originalwidth')) {
34888                 b.attr('originalwidth',  b.getSize().width);
34889             }
34890             
34891         });
34892         
34893         Roo.log(this.bricks.elements.length);
34894     },
34895     
34896     resize : function()
34897     {
34898         Roo.log('resize');
34899         var cs = this.el.getBox(true);
34900         
34901         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34902             Roo.log("no change in with or X");
34903             return;
34904         }
34905         this.currentSize = cs;
34906         this.layout();
34907     },
34908     
34909     layout : function()
34910     {
34911          Roo.log('layout');
34912         this._resetLayout();
34913         //this._manageStamps();
34914       
34915         // don't animate first layout
34916         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34917         this.layoutItems( isInstant );
34918       
34919         // flag for initalized
34920         this._isLayoutInited = true;
34921     },
34922     
34923     layoutItems : function( isInstant )
34924     {
34925         //var items = this._getItemsForLayout( this.items );
34926         // original code supports filtering layout items.. we just ignore it..
34927         
34928         this._layoutItems( this.bricks , isInstant );
34929       
34930         this._postLayout();
34931     },
34932     _layoutItems : function ( items , isInstant)
34933     {
34934        //this.fireEvent( 'layout', this, items );
34935     
34936
34937         if ( !items || !items.elements.length ) {
34938           // no items, emit event with empty array
34939             return;
34940         }
34941
34942         var queue = [];
34943         items.each(function(item) {
34944             Roo.log("layout item");
34945             Roo.log(item);
34946             // get x/y object from method
34947             var position = this._getItemLayoutPosition( item );
34948             // enqueue
34949             position.item = item;
34950             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34951             queue.push( position );
34952         }, this);
34953       
34954         this._processLayoutQueue( queue );
34955     },
34956     /** Sets position of item in DOM
34957     * @param {Element} item
34958     * @param {Number} x - horizontal position
34959     * @param {Number} y - vertical position
34960     * @param {Boolean} isInstant - disables transitions
34961     */
34962     _processLayoutQueue : function( queue )
34963     {
34964         for ( var i=0, len = queue.length; i < len; i++ ) {
34965             var obj = queue[i];
34966             obj.item.position('absolute');
34967             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34968         }
34969     },
34970       
34971     
34972     /**
34973     * Any logic you want to do after each layout,
34974     * i.e. size the container
34975     */
34976     _postLayout : function()
34977     {
34978         this.resizeContainer();
34979     },
34980     
34981     resizeContainer : function()
34982     {
34983         if ( !this.isResizingContainer ) {
34984             return;
34985         }
34986         var size = this._getContainerSize();
34987         if ( size ) {
34988             this.el.setSize(size.width,size.height);
34989             this.boxesEl.setSize(size.width,size.height);
34990         }
34991     },
34992     
34993     
34994     
34995     _resetLayout : function()
34996     {
34997         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34998         this.colWidth = this.el.getWidth();
34999         //this.gutter = this.el.getWidth(); 
35000         
35001         this.measureColumns();
35002
35003         // reset column Y
35004         var i = this.cols;
35005         this.colYs = [];
35006         while (i--) {
35007             this.colYs.push( 0 );
35008         }
35009     
35010         this.maxY = 0;
35011     },
35012
35013     measureColumns : function()
35014     {
35015         this.getContainerWidth();
35016       // if columnWidth is 0, default to outerWidth of first item
35017         if ( !this.columnWidth ) {
35018             var firstItem = this.bricks.first();
35019             Roo.log(firstItem);
35020             this.columnWidth  = this.containerWidth;
35021             if (firstItem && firstItem.attr('originalwidth') ) {
35022                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35023             }
35024             // columnWidth fall back to item of first element
35025             Roo.log("set column width?");
35026                         this.initialColumnWidth = this.columnWidth  ;
35027
35028             // if first elem has no width, default to size of container
35029             
35030         }
35031         
35032         
35033         if (this.initialColumnWidth) {
35034             this.columnWidth = this.initialColumnWidth;
35035         }
35036         
35037         
35038             
35039         // column width is fixed at the top - however if container width get's smaller we should
35040         // reduce it...
35041         
35042         // this bit calcs how man columns..
35043             
35044         var columnWidth = this.columnWidth += this.gutter;
35045       
35046         // calculate columns
35047         var containerWidth = this.containerWidth + this.gutter;
35048         
35049         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35050         // fix rounding errors, typically with gutters
35051         var excess = columnWidth - containerWidth % columnWidth;
35052         
35053         
35054         // if overshoot is less than a pixel, round up, otherwise floor it
35055         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35056         cols = Math[ mathMethod ]( cols );
35057         this.cols = Math.max( cols, 1 );
35058         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35059         
35060          // padding positioning..
35061         var totalColWidth = this.cols * this.columnWidth;
35062         var padavail = this.containerWidth - totalColWidth;
35063         // so for 2 columns - we need 3 'pads'
35064         
35065         var padNeeded = (1+this.cols) * this.padWidth;
35066         
35067         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35068         
35069         this.columnWidth += padExtra
35070         //this.padWidth = Math.floor(padavail /  ( this.cols));
35071         
35072         // adjust colum width so that padding is fixed??
35073         
35074         // we have 3 columns ... total = width * 3
35075         // we have X left over... that should be used by 
35076         
35077         //if (this.expandC) {
35078             
35079         //}
35080         
35081         
35082         
35083     },
35084     
35085     getContainerWidth : function()
35086     {
35087        /* // container is parent if fit width
35088         var container = this.isFitWidth ? this.element.parentNode : this.element;
35089         // check that this.size and size are there
35090         // IE8 triggers resize on body size change, so they might not be
35091         
35092         var size = getSize( container );  //FIXME
35093         this.containerWidth = size && size.innerWidth; //FIXME
35094         */
35095          
35096         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35097         
35098     },
35099     
35100     _getItemLayoutPosition : function( item )  // what is item?
35101     {
35102         // we resize the item to our columnWidth..
35103       
35104         item.setWidth(this.columnWidth);
35105         item.autoBoxAdjust  = false;
35106         
35107         var sz = item.getSize();
35108  
35109         // how many columns does this brick span
35110         var remainder = this.containerWidth % this.columnWidth;
35111         
35112         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35113         // round if off by 1 pixel, otherwise use ceil
35114         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35115         colSpan = Math.min( colSpan, this.cols );
35116         
35117         // normally this should be '1' as we dont' currently allow multi width columns..
35118         
35119         var colGroup = this._getColGroup( colSpan );
35120         // get the minimum Y value from the columns
35121         var minimumY = Math.min.apply( Math, colGroup );
35122         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35123         
35124         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35125          
35126         // position the brick
35127         var position = {
35128             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35129             y: this.currentSize.y + minimumY + this.padHeight
35130         };
35131         
35132         Roo.log(position);
35133         // apply setHeight to necessary columns
35134         var setHeight = minimumY + sz.height + this.padHeight;
35135         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35136         
35137         var setSpan = this.cols + 1 - colGroup.length;
35138         for ( var i = 0; i < setSpan; i++ ) {
35139           this.colYs[ shortColIndex + i ] = setHeight ;
35140         }
35141       
35142         return position;
35143     },
35144     
35145     /**
35146      * @param {Number} colSpan - number of columns the element spans
35147      * @returns {Array} colGroup
35148      */
35149     _getColGroup : function( colSpan )
35150     {
35151         if ( colSpan < 2 ) {
35152           // if brick spans only one column, use all the column Ys
35153           return this.colYs;
35154         }
35155       
35156         var colGroup = [];
35157         // how many different places could this brick fit horizontally
35158         var groupCount = this.cols + 1 - colSpan;
35159         // for each group potential horizontal position
35160         for ( var i = 0; i < groupCount; i++ ) {
35161           // make an array of colY values for that one group
35162           var groupColYs = this.colYs.slice( i, i + colSpan );
35163           // and get the max value of the array
35164           colGroup[i] = Math.max.apply( Math, groupColYs );
35165         }
35166         return colGroup;
35167     },
35168     /*
35169     _manageStamp : function( stamp )
35170     {
35171         var stampSize =  stamp.getSize();
35172         var offset = stamp.getBox();
35173         // get the columns that this stamp affects
35174         var firstX = this.isOriginLeft ? offset.x : offset.right;
35175         var lastX = firstX + stampSize.width;
35176         var firstCol = Math.floor( firstX / this.columnWidth );
35177         firstCol = Math.max( 0, firstCol );
35178         
35179         var lastCol = Math.floor( lastX / this.columnWidth );
35180         // lastCol should not go over if multiple of columnWidth #425
35181         lastCol -= lastX % this.columnWidth ? 0 : 1;
35182         lastCol = Math.min( this.cols - 1, lastCol );
35183         
35184         // set colYs to bottom of the stamp
35185         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35186             stampSize.height;
35187             
35188         for ( var i = firstCol; i <= lastCol; i++ ) {
35189           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35190         }
35191     },
35192     */
35193     
35194     _getContainerSize : function()
35195     {
35196         this.maxY = Math.max.apply( Math, this.colYs );
35197         var size = {
35198             height: this.maxY
35199         };
35200       
35201         if ( this.isFitWidth ) {
35202             size.width = this._getContainerFitWidth();
35203         }
35204       
35205         return size;
35206     },
35207     
35208     _getContainerFitWidth : function()
35209     {
35210         var unusedCols = 0;
35211         // count unused columns
35212         var i = this.cols;
35213         while ( --i ) {
35214           if ( this.colYs[i] !== 0 ) {
35215             break;
35216           }
35217           unusedCols++;
35218         }
35219         // fit container to columns that have been used
35220         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35221     },
35222     
35223     needsResizeLayout : function()
35224     {
35225         var previousWidth = this.containerWidth;
35226         this.getContainerWidth();
35227         return previousWidth !== this.containerWidth;
35228     }
35229  
35230 });
35231
35232  
35233
35234  /*
35235  * - LGPL
35236  *
35237  * element
35238  * 
35239  */
35240
35241 /**
35242  * @class Roo.bootstrap.MasonryBrick
35243  * @extends Roo.bootstrap.Component
35244  * Bootstrap MasonryBrick class
35245  * 
35246  * @constructor
35247  * Create a new MasonryBrick
35248  * @param {Object} config The config object
35249  */
35250
35251 Roo.bootstrap.MasonryBrick = function(config){
35252     
35253     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35254     
35255     Roo.bootstrap.MasonryBrick.register(this);
35256     
35257     this.addEvents({
35258         // raw events
35259         /**
35260          * @event click
35261          * When a MasonryBrick is clcik
35262          * @param {Roo.bootstrap.MasonryBrick} this
35263          * @param {Roo.EventObject} e
35264          */
35265         "click" : true
35266     });
35267 };
35268
35269 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35270     
35271     /**
35272      * @cfg {String} title
35273      */   
35274     title : '',
35275     /**
35276      * @cfg {String} html
35277      */   
35278     html : '',
35279     /**
35280      * @cfg {String} bgimage
35281      */   
35282     bgimage : '',
35283     /**
35284      * @cfg {String} videourl
35285      */   
35286     videourl : '',
35287     /**
35288      * @cfg {String} cls
35289      */   
35290     cls : '',
35291     /**
35292      * @cfg {String} href
35293      */   
35294     href : '',
35295     /**
35296      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35297      */   
35298     size : 'xs',
35299     
35300     /**
35301      * @cfg {String} placetitle (center|bottom)
35302      */   
35303     placetitle : '',
35304     
35305     /**
35306      * @cfg {Boolean} isFitContainer defalut true
35307      */   
35308     isFitContainer : true, 
35309     
35310     /**
35311      * @cfg {Boolean} preventDefault defalut false
35312      */   
35313     preventDefault : false, 
35314     
35315     /**
35316      * @cfg {Boolean} inverse defalut false
35317      */   
35318     maskInverse : false, 
35319     
35320     getAutoCreate : function()
35321     {
35322         if(!this.isFitContainer){
35323             return this.getSplitAutoCreate();
35324         }
35325         
35326         var cls = 'masonry-brick masonry-brick-full';
35327         
35328         if(this.href.length){
35329             cls += ' masonry-brick-link';
35330         }
35331         
35332         if(this.bgimage.length){
35333             cls += ' masonry-brick-image';
35334         }
35335         
35336         if(this.maskInverse){
35337             cls += ' mask-inverse';
35338         }
35339         
35340         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35341             cls += ' enable-mask';
35342         }
35343         
35344         if(this.size){
35345             cls += ' masonry-' + this.size + '-brick';
35346         }
35347         
35348         if(this.placetitle.length){
35349             
35350             switch (this.placetitle) {
35351                 case 'center' :
35352                     cls += ' masonry-center-title';
35353                     break;
35354                 case 'bottom' :
35355                     cls += ' masonry-bottom-title';
35356                     break;
35357                 default:
35358                     break;
35359             }
35360             
35361         } else {
35362             if(!this.html.length && !this.bgimage.length){
35363                 cls += ' masonry-center-title';
35364             }
35365
35366             if(!this.html.length && this.bgimage.length){
35367                 cls += ' masonry-bottom-title';
35368             }
35369         }
35370         
35371         if(this.cls){
35372             cls += ' ' + this.cls;
35373         }
35374         
35375         var cfg = {
35376             tag: (this.href.length) ? 'a' : 'div',
35377             cls: cls,
35378             cn: [
35379                 {
35380                     tag: 'div',
35381                     cls: 'masonry-brick-mask'
35382                 },
35383                 {
35384                     tag: 'div',
35385                     cls: 'masonry-brick-paragraph',
35386                     cn: []
35387                 }
35388             ]
35389         };
35390         
35391         if(this.href.length){
35392             cfg.href = this.href;
35393         }
35394         
35395         var cn = cfg.cn[1].cn;
35396         
35397         if(this.title.length){
35398             cn.push({
35399                 tag: 'h4',
35400                 cls: 'masonry-brick-title',
35401                 html: this.title
35402             });
35403         }
35404         
35405         if(this.html.length){
35406             cn.push({
35407                 tag: 'p',
35408                 cls: 'masonry-brick-text',
35409                 html: this.html
35410             });
35411         }
35412         
35413         if (!this.title.length && !this.html.length) {
35414             cfg.cn[1].cls += ' hide';
35415         }
35416         
35417         if(this.bgimage.length){
35418             cfg.cn.push({
35419                 tag: 'img',
35420                 cls: 'masonry-brick-image-view',
35421                 src: this.bgimage
35422             });
35423         }
35424         
35425         if(this.videourl.length){
35426             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35427             // youtube support only?
35428             cfg.cn.push({
35429                 tag: 'iframe',
35430                 cls: 'masonry-brick-image-view',
35431                 src: vurl,
35432                 frameborder : 0,
35433                 allowfullscreen : true
35434             });
35435         }
35436         
35437         return cfg;
35438         
35439     },
35440     
35441     getSplitAutoCreate : function()
35442     {
35443         var cls = 'masonry-brick masonry-brick-split';
35444         
35445         if(this.href.length){
35446             cls += ' masonry-brick-link';
35447         }
35448         
35449         if(this.bgimage.length){
35450             cls += ' masonry-brick-image';
35451         }
35452         
35453         if(this.size){
35454             cls += ' masonry-' + this.size + '-brick';
35455         }
35456         
35457         switch (this.placetitle) {
35458             case 'center' :
35459                 cls += ' masonry-center-title';
35460                 break;
35461             case 'bottom' :
35462                 cls += ' masonry-bottom-title';
35463                 break;
35464             default:
35465                 if(!this.bgimage.length){
35466                     cls += ' masonry-center-title';
35467                 }
35468
35469                 if(this.bgimage.length){
35470                     cls += ' masonry-bottom-title';
35471                 }
35472                 break;
35473         }
35474         
35475         if(this.cls){
35476             cls += ' ' + this.cls;
35477         }
35478         
35479         var cfg = {
35480             tag: (this.href.length) ? 'a' : 'div',
35481             cls: cls,
35482             cn: [
35483                 {
35484                     tag: 'div',
35485                     cls: 'masonry-brick-split-head',
35486                     cn: [
35487                         {
35488                             tag: 'div',
35489                             cls: 'masonry-brick-paragraph',
35490                             cn: []
35491                         }
35492                     ]
35493                 },
35494                 {
35495                     tag: 'div',
35496                     cls: 'masonry-brick-split-body',
35497                     cn: []
35498                 }
35499             ]
35500         };
35501         
35502         if(this.href.length){
35503             cfg.href = this.href;
35504         }
35505         
35506         if(this.title.length){
35507             cfg.cn[0].cn[0].cn.push({
35508                 tag: 'h4',
35509                 cls: 'masonry-brick-title',
35510                 html: this.title
35511             });
35512         }
35513         
35514         if(this.html.length){
35515             cfg.cn[1].cn.push({
35516                 tag: 'p',
35517                 cls: 'masonry-brick-text',
35518                 html: this.html
35519             });
35520         }
35521
35522         if(this.bgimage.length){
35523             cfg.cn[0].cn.push({
35524                 tag: 'img',
35525                 cls: 'masonry-brick-image-view',
35526                 src: this.bgimage
35527             });
35528         }
35529         
35530         if(this.videourl.length){
35531             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35532             // youtube support only?
35533             cfg.cn[0].cn.cn.push({
35534                 tag: 'iframe',
35535                 cls: 'masonry-brick-image-view',
35536                 src: vurl,
35537                 frameborder : 0,
35538                 allowfullscreen : true
35539             });
35540         }
35541         
35542         return cfg;
35543     },
35544     
35545     initEvents: function() 
35546     {
35547         switch (this.size) {
35548             case 'xs' :
35549                 this.x = 1;
35550                 this.y = 1;
35551                 break;
35552             case 'sm' :
35553                 this.x = 2;
35554                 this.y = 2;
35555                 break;
35556             case 'md' :
35557             case 'md-left' :
35558             case 'md-right' :
35559                 this.x = 3;
35560                 this.y = 3;
35561                 break;
35562             case 'tall' :
35563                 this.x = 2;
35564                 this.y = 3;
35565                 break;
35566             case 'wide' :
35567                 this.x = 3;
35568                 this.y = 2;
35569                 break;
35570             case 'wide-thin' :
35571                 this.x = 3;
35572                 this.y = 1;
35573                 break;
35574                         
35575             default :
35576                 break;
35577         }
35578         
35579         if(Roo.isTouch){
35580             this.el.on('touchstart', this.onTouchStart, this);
35581             this.el.on('touchmove', this.onTouchMove, this);
35582             this.el.on('touchend', this.onTouchEnd, this);
35583             this.el.on('contextmenu', this.onContextMenu, this);
35584         } else {
35585             this.el.on('mouseenter'  ,this.enter, this);
35586             this.el.on('mouseleave', this.leave, this);
35587             this.el.on('click', this.onClick, this);
35588         }
35589         
35590         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35591             this.parent().bricks.push(this);   
35592         }
35593         
35594     },
35595     
35596     onClick: function(e, el)
35597     {
35598         var time = this.endTimer - this.startTimer;
35599         // Roo.log(e.preventDefault());
35600         if(Roo.isTouch){
35601             if(time > 1000){
35602                 e.preventDefault();
35603                 return;
35604             }
35605         }
35606         
35607         if(!this.preventDefault){
35608             return;
35609         }
35610         
35611         e.preventDefault();
35612         
35613         if (this.activeClass != '') {
35614             this.selectBrick();
35615         }
35616         
35617         this.fireEvent('click', this, e);
35618     },
35619     
35620     enter: function(e, el)
35621     {
35622         e.preventDefault();
35623         
35624         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35625             return;
35626         }
35627         
35628         if(this.bgimage.length && this.html.length){
35629             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35630         }
35631     },
35632     
35633     leave: function(e, el)
35634     {
35635         e.preventDefault();
35636         
35637         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35638             return;
35639         }
35640         
35641         if(this.bgimage.length && this.html.length){
35642             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35643         }
35644     },
35645     
35646     onTouchStart: function(e, el)
35647     {
35648 //        e.preventDefault();
35649         
35650         this.touchmoved = false;
35651         
35652         if(!this.isFitContainer){
35653             return;
35654         }
35655         
35656         if(!this.bgimage.length || !this.html.length){
35657             return;
35658         }
35659         
35660         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35661         
35662         this.timer = new Date().getTime();
35663         
35664     },
35665     
35666     onTouchMove: function(e, el)
35667     {
35668         this.touchmoved = true;
35669     },
35670     
35671     onContextMenu : function(e,el)
35672     {
35673         e.preventDefault();
35674         e.stopPropagation();
35675         return false;
35676     },
35677     
35678     onTouchEnd: function(e, el)
35679     {
35680 //        e.preventDefault();
35681         
35682         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35683         
35684             this.leave(e,el);
35685             
35686             return;
35687         }
35688         
35689         if(!this.bgimage.length || !this.html.length){
35690             
35691             if(this.href.length){
35692                 window.location.href = this.href;
35693             }
35694             
35695             return;
35696         }
35697         
35698         if(!this.isFitContainer){
35699             return;
35700         }
35701         
35702         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35703         
35704         window.location.href = this.href;
35705     },
35706     
35707     //selection on single brick only
35708     selectBrick : function() {
35709         
35710         if (!this.parentId) {
35711             return;
35712         }
35713         
35714         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35715         var index = m.selectedBrick.indexOf(this.id);
35716         
35717         if ( index > -1) {
35718             m.selectedBrick.splice(index,1);
35719             this.el.removeClass(this.activeClass);
35720             return;
35721         }
35722         
35723         for(var i = 0; i < m.selectedBrick.length; i++) {
35724             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35725             b.el.removeClass(b.activeClass);
35726         }
35727         
35728         m.selectedBrick = [];
35729         
35730         m.selectedBrick.push(this.id);
35731         this.el.addClass(this.activeClass);
35732         return;
35733     },
35734     
35735     isSelected : function(){
35736         return this.el.hasClass(this.activeClass);
35737         
35738     }
35739 });
35740
35741 Roo.apply(Roo.bootstrap.MasonryBrick, {
35742     
35743     //groups: {},
35744     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35745      /**
35746     * register a Masonry Brick
35747     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35748     */
35749     
35750     register : function(brick)
35751     {
35752         //this.groups[brick.id] = brick;
35753         this.groups.add(brick.id, brick);
35754     },
35755     /**
35756     * fetch a  masonry brick based on the masonry brick ID
35757     * @param {string} the masonry brick to add
35758     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35759     */
35760     
35761     get: function(brick_id) 
35762     {
35763         // if (typeof(this.groups[brick_id]) == 'undefined') {
35764         //     return false;
35765         // }
35766         // return this.groups[brick_id] ;
35767         
35768         if(this.groups.key(brick_id)) {
35769             return this.groups.key(brick_id);
35770         }
35771         
35772         return false;
35773     }
35774     
35775     
35776     
35777 });
35778
35779  /*
35780  * - LGPL
35781  *
35782  * element
35783  * 
35784  */
35785
35786 /**
35787  * @class Roo.bootstrap.Brick
35788  * @extends Roo.bootstrap.Component
35789  * Bootstrap Brick class
35790  * 
35791  * @constructor
35792  * Create a new Brick
35793  * @param {Object} config The config object
35794  */
35795
35796 Roo.bootstrap.Brick = function(config){
35797     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35798     
35799     this.addEvents({
35800         // raw events
35801         /**
35802          * @event click
35803          * When a Brick is click
35804          * @param {Roo.bootstrap.Brick} this
35805          * @param {Roo.EventObject} e
35806          */
35807         "click" : true
35808     });
35809 };
35810
35811 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35812     
35813     /**
35814      * @cfg {String} title
35815      */   
35816     title : '',
35817     /**
35818      * @cfg {String} html
35819      */   
35820     html : '',
35821     /**
35822      * @cfg {String} bgimage
35823      */   
35824     bgimage : '',
35825     /**
35826      * @cfg {String} cls
35827      */   
35828     cls : '',
35829     /**
35830      * @cfg {String} href
35831      */   
35832     href : '',
35833     /**
35834      * @cfg {String} video
35835      */   
35836     video : '',
35837     /**
35838      * @cfg {Boolean} square
35839      */   
35840     square : true,
35841     
35842     getAutoCreate : function()
35843     {
35844         var cls = 'roo-brick';
35845         
35846         if(this.href.length){
35847             cls += ' roo-brick-link';
35848         }
35849         
35850         if(this.bgimage.length){
35851             cls += ' roo-brick-image';
35852         }
35853         
35854         if(!this.html.length && !this.bgimage.length){
35855             cls += ' roo-brick-center-title';
35856         }
35857         
35858         if(!this.html.length && this.bgimage.length){
35859             cls += ' roo-brick-bottom-title';
35860         }
35861         
35862         if(this.cls){
35863             cls += ' ' + this.cls;
35864         }
35865         
35866         var cfg = {
35867             tag: (this.href.length) ? 'a' : 'div',
35868             cls: cls,
35869             cn: [
35870                 {
35871                     tag: 'div',
35872                     cls: 'roo-brick-paragraph',
35873                     cn: []
35874                 }
35875             ]
35876         };
35877         
35878         if(this.href.length){
35879             cfg.href = this.href;
35880         }
35881         
35882         var cn = cfg.cn[0].cn;
35883         
35884         if(this.title.length){
35885             cn.push({
35886                 tag: 'h4',
35887                 cls: 'roo-brick-title',
35888                 html: this.title
35889             });
35890         }
35891         
35892         if(this.html.length){
35893             cn.push({
35894                 tag: 'p',
35895                 cls: 'roo-brick-text',
35896                 html: this.html
35897             });
35898         } else {
35899             cn.cls += ' hide';
35900         }
35901         
35902         if(this.bgimage.length){
35903             cfg.cn.push({
35904                 tag: 'img',
35905                 cls: 'roo-brick-image-view',
35906                 src: this.bgimage
35907             });
35908         }
35909         
35910         return cfg;
35911     },
35912     
35913     initEvents: function() 
35914     {
35915         if(this.title.length || this.html.length){
35916             this.el.on('mouseenter'  ,this.enter, this);
35917             this.el.on('mouseleave', this.leave, this);
35918         }
35919         
35920         Roo.EventManager.onWindowResize(this.resize, this); 
35921         
35922         if(this.bgimage.length){
35923             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35924             this.imageEl.on('load', this.onImageLoad, this);
35925             return;
35926         }
35927         
35928         this.resize();
35929     },
35930     
35931     onImageLoad : function()
35932     {
35933         this.resize();
35934     },
35935     
35936     resize : function()
35937     {
35938         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35939         
35940         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35941         
35942         if(this.bgimage.length){
35943             var image = this.el.select('.roo-brick-image-view', true).first();
35944             
35945             image.setWidth(paragraph.getWidth());
35946             
35947             if(this.square){
35948                 image.setHeight(paragraph.getWidth());
35949             }
35950             
35951             this.el.setHeight(image.getHeight());
35952             paragraph.setHeight(image.getHeight());
35953             
35954         }
35955         
35956     },
35957     
35958     enter: function(e, el)
35959     {
35960         e.preventDefault();
35961         
35962         if(this.bgimage.length){
35963             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35964             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35965         }
35966     },
35967     
35968     leave: function(e, el)
35969     {
35970         e.preventDefault();
35971         
35972         if(this.bgimage.length){
35973             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35974             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35975         }
35976     }
35977     
35978 });
35979
35980  
35981
35982  /*
35983  * - LGPL
35984  *
35985  * Number field 
35986  */
35987
35988 /**
35989  * @class Roo.bootstrap.NumberField
35990  * @extends Roo.bootstrap.Input
35991  * Bootstrap NumberField class
35992  * 
35993  * 
35994  * 
35995  * 
35996  * @constructor
35997  * Create a new NumberField
35998  * @param {Object} config The config object
35999  */
36000
36001 Roo.bootstrap.NumberField = function(config){
36002     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
36003 };
36004
36005 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
36006     
36007     /**
36008      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
36009      */
36010     allowDecimals : true,
36011     /**
36012      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
36013      */
36014     decimalSeparator : ".",
36015     /**
36016      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
36017      */
36018     decimalPrecision : 2,
36019     /**
36020      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36021      */
36022     allowNegative : true,
36023     
36024     /**
36025      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36026      */
36027     allowZero: true,
36028     /**
36029      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36030      */
36031     minValue : Number.NEGATIVE_INFINITY,
36032     /**
36033      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36034      */
36035     maxValue : Number.MAX_VALUE,
36036     /**
36037      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36038      */
36039     minText : "The minimum value for this field is {0}",
36040     /**
36041      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36042      */
36043     maxText : "The maximum value for this field is {0}",
36044     /**
36045      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36046      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36047      */
36048     nanText : "{0} is not a valid number",
36049     /**
36050      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36051      */
36052     thousandsDelimiter : false,
36053     /**
36054      * @cfg {String} valueAlign alignment of value
36055      */
36056     valueAlign : "left",
36057
36058     getAutoCreate : function()
36059     {
36060         var hiddenInput = {
36061             tag: 'input',
36062             type: 'hidden',
36063             id: Roo.id(),
36064             cls: 'hidden-number-input'
36065         };
36066         
36067         if (this.name) {
36068             hiddenInput.name = this.name;
36069         }
36070         
36071         this.name = '';
36072         
36073         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36074         
36075         this.name = hiddenInput.name;
36076         
36077         if(cfg.cn.length > 0) {
36078             cfg.cn.push(hiddenInput);
36079         }
36080         
36081         return cfg;
36082     },
36083
36084     // private
36085     initEvents : function()
36086     {   
36087         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36088         
36089         var allowed = "0123456789";
36090         
36091         if(this.allowDecimals){
36092             allowed += this.decimalSeparator;
36093         }
36094         
36095         if(this.allowNegative){
36096             allowed += "-";
36097         }
36098         
36099         if(this.thousandsDelimiter) {
36100             allowed += ",";
36101         }
36102         
36103         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36104         
36105         var keyPress = function(e){
36106             
36107             var k = e.getKey();
36108             
36109             var c = e.getCharCode();
36110             
36111             if(
36112                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36113                     allowed.indexOf(String.fromCharCode(c)) === -1
36114             ){
36115                 e.stopEvent();
36116                 return;
36117             }
36118             
36119             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36120                 return;
36121             }
36122             
36123             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36124                 e.stopEvent();
36125             }
36126         };
36127         
36128         this.el.on("keypress", keyPress, this);
36129     },
36130     
36131     validateValue : function(value)
36132     {
36133         
36134         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36135             return false;
36136         }
36137         
36138         var num = this.parseValue(value);
36139         
36140         if(isNaN(num)){
36141             this.markInvalid(String.format(this.nanText, value));
36142             return false;
36143         }
36144         
36145         if(num < this.minValue){
36146             this.markInvalid(String.format(this.minText, this.minValue));
36147             return false;
36148         }
36149         
36150         if(num > this.maxValue){
36151             this.markInvalid(String.format(this.maxText, this.maxValue));
36152             return false;
36153         }
36154         
36155         return true;
36156     },
36157
36158     getValue : function()
36159     {
36160         var v = this.hiddenEl().getValue();
36161         
36162         return this.fixPrecision(this.parseValue(v));
36163     },
36164
36165     parseValue : function(value)
36166     {
36167         if(this.thousandsDelimiter) {
36168             value += "";
36169             r = new RegExp(",", "g");
36170             value = value.replace(r, "");
36171         }
36172         
36173         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36174         return isNaN(value) ? '' : value;
36175     },
36176
36177     fixPrecision : function(value)
36178     {
36179         if(this.thousandsDelimiter) {
36180             value += "";
36181             r = new RegExp(",", "g");
36182             value = value.replace(r, "");
36183         }
36184         
36185         var nan = isNaN(value);
36186         
36187         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36188             return nan ? '' : value;
36189         }
36190         return parseFloat(value).toFixed(this.decimalPrecision);
36191     },
36192
36193     setValue : function(v)
36194     {
36195         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36196         
36197         this.value = v;
36198         
36199         if(this.rendered){
36200             
36201             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36202             
36203             this.inputEl().dom.value = (v == '') ? '' :
36204                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36205             
36206             if(!this.allowZero && v === '0') {
36207                 this.hiddenEl().dom.value = '';
36208                 this.inputEl().dom.value = '';
36209             }
36210             
36211             this.validate();
36212         }
36213     },
36214
36215     decimalPrecisionFcn : function(v)
36216     {
36217         return Math.floor(v);
36218     },
36219
36220     beforeBlur : function()
36221     {
36222         var v = this.parseValue(this.getRawValue());
36223         
36224         if(v || v === 0 || v === ''){
36225             this.setValue(v);
36226         }
36227     },
36228     
36229     hiddenEl : function()
36230     {
36231         return this.el.select('input.hidden-number-input',true).first();
36232     }
36233     
36234 });
36235
36236  
36237
36238 /*
36239 * Licence: LGPL
36240 */
36241
36242 /**
36243  * @class Roo.bootstrap.DocumentSlider
36244  * @extends Roo.bootstrap.Component
36245  * Bootstrap DocumentSlider class
36246  * 
36247  * @constructor
36248  * Create a new DocumentViewer
36249  * @param {Object} config The config object
36250  */
36251
36252 Roo.bootstrap.DocumentSlider = function(config){
36253     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36254     
36255     this.files = [];
36256     
36257     this.addEvents({
36258         /**
36259          * @event initial
36260          * Fire after initEvent
36261          * @param {Roo.bootstrap.DocumentSlider} this
36262          */
36263         "initial" : true,
36264         /**
36265          * @event update
36266          * Fire after update
36267          * @param {Roo.bootstrap.DocumentSlider} this
36268          */
36269         "update" : true,
36270         /**
36271          * @event click
36272          * Fire after click
36273          * @param {Roo.bootstrap.DocumentSlider} this
36274          */
36275         "click" : true
36276     });
36277 };
36278
36279 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36280     
36281     files : false,
36282     
36283     indicator : 0,
36284     
36285     getAutoCreate : function()
36286     {
36287         var cfg = {
36288             tag : 'div',
36289             cls : 'roo-document-slider',
36290             cn : [
36291                 {
36292                     tag : 'div',
36293                     cls : 'roo-document-slider-header',
36294                     cn : [
36295                         {
36296                             tag : 'div',
36297                             cls : 'roo-document-slider-header-title'
36298                         }
36299                     ]
36300                 },
36301                 {
36302                     tag : 'div',
36303                     cls : 'roo-document-slider-body',
36304                     cn : [
36305                         {
36306                             tag : 'div',
36307                             cls : 'roo-document-slider-prev',
36308                             cn : [
36309                                 {
36310                                     tag : 'i',
36311                                     cls : 'fa fa-chevron-left'
36312                                 }
36313                             ]
36314                         },
36315                         {
36316                             tag : 'div',
36317                             cls : 'roo-document-slider-thumb',
36318                             cn : [
36319                                 {
36320                                     tag : 'img',
36321                                     cls : 'roo-document-slider-image'
36322                                 }
36323                             ]
36324                         },
36325                         {
36326                             tag : 'div',
36327                             cls : 'roo-document-slider-next',
36328                             cn : [
36329                                 {
36330                                     tag : 'i',
36331                                     cls : 'fa fa-chevron-right'
36332                                 }
36333                             ]
36334                         }
36335                     ]
36336                 }
36337             ]
36338         };
36339         
36340         return cfg;
36341     },
36342     
36343     initEvents : function()
36344     {
36345         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36346         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36347         
36348         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36349         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36350         
36351         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36352         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36353         
36354         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36355         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36356         
36357         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36358         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36359         
36360         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36361         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36362         
36363         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36364         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36365         
36366         this.thumbEl.on('click', this.onClick, this);
36367         
36368         this.prevIndicator.on('click', this.prev, this);
36369         
36370         this.nextIndicator.on('click', this.next, this);
36371         
36372     },
36373     
36374     initial : function()
36375     {
36376         if(this.files.length){
36377             this.indicator = 1;
36378             this.update()
36379         }
36380         
36381         this.fireEvent('initial', this);
36382     },
36383     
36384     update : function()
36385     {
36386         this.imageEl.attr('src', this.files[this.indicator - 1]);
36387         
36388         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36389         
36390         this.prevIndicator.show();
36391         
36392         if(this.indicator == 1){
36393             this.prevIndicator.hide();
36394         }
36395         
36396         this.nextIndicator.show();
36397         
36398         if(this.indicator == this.files.length){
36399             this.nextIndicator.hide();
36400         }
36401         
36402         this.thumbEl.scrollTo('top');
36403         
36404         this.fireEvent('update', this);
36405     },
36406     
36407     onClick : function(e)
36408     {
36409         e.preventDefault();
36410         
36411         this.fireEvent('click', this);
36412     },
36413     
36414     prev : function(e)
36415     {
36416         e.preventDefault();
36417         
36418         this.indicator = Math.max(1, this.indicator - 1);
36419         
36420         this.update();
36421     },
36422     
36423     next : function(e)
36424     {
36425         e.preventDefault();
36426         
36427         this.indicator = Math.min(this.files.length, this.indicator + 1);
36428         
36429         this.update();
36430     }
36431 });
36432 /*
36433  * - LGPL
36434  *
36435  * RadioSet
36436  *
36437  *
36438  */
36439
36440 /**
36441  * @class Roo.bootstrap.RadioSet
36442  * @extends Roo.bootstrap.Input
36443  * Bootstrap RadioSet class
36444  * @cfg {String} indicatorpos (left|right) default left
36445  * @cfg {Boolean} inline (true|false) inline the element (default true)
36446  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36447  * @constructor
36448  * Create a new RadioSet
36449  * @param {Object} config The config object
36450  */
36451
36452 Roo.bootstrap.RadioSet = function(config){
36453     
36454     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36455     
36456     this.radioes = [];
36457     
36458     Roo.bootstrap.RadioSet.register(this);
36459     
36460     this.addEvents({
36461         /**
36462         * @event check
36463         * Fires when the element is checked or unchecked.
36464         * @param {Roo.bootstrap.RadioSet} this This radio
36465         * @param {Roo.bootstrap.Radio} item The checked item
36466         */
36467        check : true,
36468        /**
36469         * @event click
36470         * Fires when the element is click.
36471         * @param {Roo.bootstrap.RadioSet} this This radio set
36472         * @param {Roo.bootstrap.Radio} item The checked item
36473         * @param {Roo.EventObject} e The event object
36474         */
36475        click : true
36476     });
36477     
36478 };
36479
36480 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36481
36482     radioes : false,
36483     
36484     inline : true,
36485     
36486     weight : '',
36487     
36488     indicatorpos : 'left',
36489     
36490     getAutoCreate : function()
36491     {
36492         var label = {
36493             tag : 'label',
36494             cls : 'roo-radio-set-label',
36495             cn : [
36496                 {
36497                     tag : 'span',
36498                     html : this.fieldLabel
36499                 }
36500             ]
36501         };
36502         if (Roo.bootstrap.version == 3) {
36503             
36504             
36505             if(this.indicatorpos == 'left'){
36506                 label.cn.unshift({
36507                     tag : 'i',
36508                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36509                     tooltip : 'This field is required'
36510                 });
36511             } else {
36512                 label.cn.push({
36513                     tag : 'i',
36514                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36515                     tooltip : 'This field is required'
36516                 });
36517             }
36518         }
36519         var items = {
36520             tag : 'div',
36521             cls : 'roo-radio-set-items'
36522         };
36523         
36524         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36525         
36526         if (align === 'left' && this.fieldLabel.length) {
36527             
36528             items = {
36529                 cls : "roo-radio-set-right", 
36530                 cn: [
36531                     items
36532                 ]
36533             };
36534             
36535             if(this.labelWidth > 12){
36536                 label.style = "width: " + this.labelWidth + 'px';
36537             }
36538             
36539             if(this.labelWidth < 13 && this.labelmd == 0){
36540                 this.labelmd = this.labelWidth;
36541             }
36542             
36543             if(this.labellg > 0){
36544                 label.cls += ' col-lg-' + this.labellg;
36545                 items.cls += ' col-lg-' + (12 - this.labellg);
36546             }
36547             
36548             if(this.labelmd > 0){
36549                 label.cls += ' col-md-' + this.labelmd;
36550                 items.cls += ' col-md-' + (12 - this.labelmd);
36551             }
36552             
36553             if(this.labelsm > 0){
36554                 label.cls += ' col-sm-' + this.labelsm;
36555                 items.cls += ' col-sm-' + (12 - this.labelsm);
36556             }
36557             
36558             if(this.labelxs > 0){
36559                 label.cls += ' col-xs-' + this.labelxs;
36560                 items.cls += ' col-xs-' + (12 - this.labelxs);
36561             }
36562         }
36563         
36564         var cfg = {
36565             tag : 'div',
36566             cls : 'roo-radio-set',
36567             cn : [
36568                 {
36569                     tag : 'input',
36570                     cls : 'roo-radio-set-input',
36571                     type : 'hidden',
36572                     name : this.name,
36573                     value : this.value ? this.value :  ''
36574                 },
36575                 label,
36576                 items
36577             ]
36578         };
36579         
36580         if(this.weight.length){
36581             cfg.cls += ' roo-radio-' + this.weight;
36582         }
36583         
36584         if(this.inline) {
36585             cfg.cls += ' roo-radio-set-inline';
36586         }
36587         
36588         var settings=this;
36589         ['xs','sm','md','lg'].map(function(size){
36590             if (settings[size]) {
36591                 cfg.cls += ' col-' + size + '-' + settings[size];
36592             }
36593         });
36594         
36595         return cfg;
36596         
36597     },
36598
36599     initEvents : function()
36600     {
36601         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36602         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36603         
36604         if(!this.fieldLabel.length){
36605             this.labelEl.hide();
36606         }
36607         
36608         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36609         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36610         
36611         this.indicator = this.indicatorEl();
36612         
36613         if(this.indicator){
36614             this.indicator.addClass('invisible');
36615         }
36616         
36617         this.originalValue = this.getValue();
36618         
36619     },
36620     
36621     inputEl: function ()
36622     {
36623         return this.el.select('.roo-radio-set-input', true).first();
36624     },
36625     
36626     getChildContainer : function()
36627     {
36628         return this.itemsEl;
36629     },
36630     
36631     register : function(item)
36632     {
36633         this.radioes.push(item);
36634         
36635     },
36636     
36637     validate : function()
36638     {   
36639         if(this.getVisibilityEl().hasClass('hidden')){
36640             return true;
36641         }
36642         
36643         var valid = false;
36644         
36645         Roo.each(this.radioes, function(i){
36646             if(!i.checked){
36647                 return;
36648             }
36649             
36650             valid = true;
36651             return false;
36652         });
36653         
36654         if(this.allowBlank) {
36655             return true;
36656         }
36657         
36658         if(this.disabled || valid){
36659             this.markValid();
36660             return true;
36661         }
36662         
36663         this.markInvalid();
36664         return false;
36665         
36666     },
36667     
36668     markValid : function()
36669     {
36670         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36671             this.indicatorEl().removeClass('visible');
36672             this.indicatorEl().addClass('invisible');
36673         }
36674         
36675         
36676         if (Roo.bootstrap.version == 3) {
36677             this.el.removeClass([this.invalidClass, this.validClass]);
36678             this.el.addClass(this.validClass);
36679         } else {
36680             this.el.removeClass(['is-invalid','is-valid']);
36681             this.el.addClass(['is-valid']);
36682         }
36683         this.fireEvent('valid', this);
36684     },
36685     
36686     markInvalid : function(msg)
36687     {
36688         if(this.allowBlank || this.disabled){
36689             return;
36690         }
36691         
36692         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36693             this.indicatorEl().removeClass('invisible');
36694             this.indicatorEl().addClass('visible');
36695         }
36696         if (Roo.bootstrap.version == 3) {
36697             this.el.removeClass([this.invalidClass, this.validClass]);
36698             this.el.addClass(this.invalidClass);
36699         } else {
36700             this.el.removeClass(['is-invalid','is-valid']);
36701             this.el.addClass(['is-invalid']);
36702         }
36703         
36704         this.fireEvent('invalid', this, msg);
36705         
36706     },
36707     
36708     setValue : function(v, suppressEvent)
36709     {   
36710         if(this.value === v){
36711             return;
36712         }
36713         
36714         this.value = v;
36715         
36716         if(this.rendered){
36717             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36718         }
36719         
36720         Roo.each(this.radioes, function(i){
36721             i.checked = false;
36722             i.el.removeClass('checked');
36723         });
36724         
36725         Roo.each(this.radioes, function(i){
36726             
36727             if(i.value === v || i.value.toString() === v.toString()){
36728                 i.checked = true;
36729                 i.el.addClass('checked');
36730                 
36731                 if(suppressEvent !== true){
36732                     this.fireEvent('check', this, i);
36733                 }
36734                 
36735                 return false;
36736             }
36737             
36738         }, this);
36739         
36740         this.validate();
36741     },
36742     
36743     clearInvalid : function(){
36744         
36745         if(!this.el || this.preventMark){
36746             return;
36747         }
36748         
36749         this.el.removeClass([this.invalidClass]);
36750         
36751         this.fireEvent('valid', this);
36752     }
36753     
36754 });
36755
36756 Roo.apply(Roo.bootstrap.RadioSet, {
36757     
36758     groups: {},
36759     
36760     register : function(set)
36761     {
36762         this.groups[set.name] = set;
36763     },
36764     
36765     get: function(name) 
36766     {
36767         if (typeof(this.groups[name]) == 'undefined') {
36768             return false;
36769         }
36770         
36771         return this.groups[name] ;
36772     }
36773     
36774 });
36775 /*
36776  * Based on:
36777  * Ext JS Library 1.1.1
36778  * Copyright(c) 2006-2007, Ext JS, LLC.
36779  *
36780  * Originally Released Under LGPL - original licence link has changed is not relivant.
36781  *
36782  * Fork - LGPL
36783  * <script type="text/javascript">
36784  */
36785
36786
36787 /**
36788  * @class Roo.bootstrap.SplitBar
36789  * @extends Roo.util.Observable
36790  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36791  * <br><br>
36792  * Usage:
36793  * <pre><code>
36794 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36795                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36796 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36797 split.minSize = 100;
36798 split.maxSize = 600;
36799 split.animate = true;
36800 split.on('moved', splitterMoved);
36801 </code></pre>
36802  * @constructor
36803  * Create a new SplitBar
36804  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36805  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36806  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36807  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36808                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36809                         position of the SplitBar).
36810  */
36811 Roo.bootstrap.SplitBar = function(cfg){
36812     
36813     /** @private */
36814     
36815     //{
36816     //  dragElement : elm
36817     //  resizingElement: el,
36818         // optional..
36819     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36820     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36821         // existingProxy ???
36822     //}
36823     
36824     this.el = Roo.get(cfg.dragElement, true);
36825     this.el.dom.unselectable = "on";
36826     /** @private */
36827     this.resizingEl = Roo.get(cfg.resizingElement, true);
36828
36829     /**
36830      * @private
36831      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36832      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36833      * @type Number
36834      */
36835     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36836     
36837     /**
36838      * The minimum size of the resizing element. (Defaults to 0)
36839      * @type Number
36840      */
36841     this.minSize = 0;
36842     
36843     /**
36844      * The maximum size of the resizing element. (Defaults to 2000)
36845      * @type Number
36846      */
36847     this.maxSize = 2000;
36848     
36849     /**
36850      * Whether to animate the transition to the new size
36851      * @type Boolean
36852      */
36853     this.animate = false;
36854     
36855     /**
36856      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36857      * @type Boolean
36858      */
36859     this.useShim = false;
36860     
36861     /** @private */
36862     this.shim = null;
36863     
36864     if(!cfg.existingProxy){
36865         /** @private */
36866         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36867     }else{
36868         this.proxy = Roo.get(cfg.existingProxy).dom;
36869     }
36870     /** @private */
36871     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36872     
36873     /** @private */
36874     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36875     
36876     /** @private */
36877     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36878     
36879     /** @private */
36880     this.dragSpecs = {};
36881     
36882     /**
36883      * @private The adapter to use to positon and resize elements
36884      */
36885     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36886     this.adapter.init(this);
36887     
36888     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36889         /** @private */
36890         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36891         this.el.addClass("roo-splitbar-h");
36892     }else{
36893         /** @private */
36894         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36895         this.el.addClass("roo-splitbar-v");
36896     }
36897     
36898     this.addEvents({
36899         /**
36900          * @event resize
36901          * Fires when the splitter is moved (alias for {@link #event-moved})
36902          * @param {Roo.bootstrap.SplitBar} this
36903          * @param {Number} newSize the new width or height
36904          */
36905         "resize" : true,
36906         /**
36907          * @event moved
36908          * Fires when the splitter is moved
36909          * @param {Roo.bootstrap.SplitBar} this
36910          * @param {Number} newSize the new width or height
36911          */
36912         "moved" : true,
36913         /**
36914          * @event beforeresize
36915          * Fires before the splitter is dragged
36916          * @param {Roo.bootstrap.SplitBar} this
36917          */
36918         "beforeresize" : true,
36919
36920         "beforeapply" : true
36921     });
36922
36923     Roo.util.Observable.call(this);
36924 };
36925
36926 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36927     onStartProxyDrag : function(x, y){
36928         this.fireEvent("beforeresize", this);
36929         if(!this.overlay){
36930             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36931             o.unselectable();
36932             o.enableDisplayMode("block");
36933             // all splitbars share the same overlay
36934             Roo.bootstrap.SplitBar.prototype.overlay = o;
36935         }
36936         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36937         this.overlay.show();
36938         Roo.get(this.proxy).setDisplayed("block");
36939         var size = this.adapter.getElementSize(this);
36940         this.activeMinSize = this.getMinimumSize();;
36941         this.activeMaxSize = this.getMaximumSize();;
36942         var c1 = size - this.activeMinSize;
36943         var c2 = Math.max(this.activeMaxSize - size, 0);
36944         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36945             this.dd.resetConstraints();
36946             this.dd.setXConstraint(
36947                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36948                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36949             );
36950             this.dd.setYConstraint(0, 0);
36951         }else{
36952             this.dd.resetConstraints();
36953             this.dd.setXConstraint(0, 0);
36954             this.dd.setYConstraint(
36955                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36956                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36957             );
36958          }
36959         this.dragSpecs.startSize = size;
36960         this.dragSpecs.startPoint = [x, y];
36961         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36962     },
36963     
36964     /** 
36965      * @private Called after the drag operation by the DDProxy
36966      */
36967     onEndProxyDrag : function(e){
36968         Roo.get(this.proxy).setDisplayed(false);
36969         var endPoint = Roo.lib.Event.getXY(e);
36970         if(this.overlay){
36971             this.overlay.hide();
36972         }
36973         var newSize;
36974         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36975             newSize = this.dragSpecs.startSize + 
36976                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36977                     endPoint[0] - this.dragSpecs.startPoint[0] :
36978                     this.dragSpecs.startPoint[0] - endPoint[0]
36979                 );
36980         }else{
36981             newSize = this.dragSpecs.startSize + 
36982                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36983                     endPoint[1] - this.dragSpecs.startPoint[1] :
36984                     this.dragSpecs.startPoint[1] - endPoint[1]
36985                 );
36986         }
36987         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36988         if(newSize != this.dragSpecs.startSize){
36989             if(this.fireEvent('beforeapply', this, newSize) !== false){
36990                 this.adapter.setElementSize(this, newSize);
36991                 this.fireEvent("moved", this, newSize);
36992                 this.fireEvent("resize", this, newSize);
36993             }
36994         }
36995     },
36996     
36997     /**
36998      * Get the adapter this SplitBar uses
36999      * @return The adapter object
37000      */
37001     getAdapter : function(){
37002         return this.adapter;
37003     },
37004     
37005     /**
37006      * Set the adapter this SplitBar uses
37007      * @param {Object} adapter A SplitBar adapter object
37008      */
37009     setAdapter : function(adapter){
37010         this.adapter = adapter;
37011         this.adapter.init(this);
37012     },
37013     
37014     /**
37015      * Gets the minimum size for the resizing element
37016      * @return {Number} The minimum size
37017      */
37018     getMinimumSize : function(){
37019         return this.minSize;
37020     },
37021     
37022     /**
37023      * Sets the minimum size for the resizing element
37024      * @param {Number} minSize The minimum size
37025      */
37026     setMinimumSize : function(minSize){
37027         this.minSize = minSize;
37028     },
37029     
37030     /**
37031      * Gets the maximum size for the resizing element
37032      * @return {Number} The maximum size
37033      */
37034     getMaximumSize : function(){
37035         return this.maxSize;
37036     },
37037     
37038     /**
37039      * Sets the maximum size for the resizing element
37040      * @param {Number} maxSize The maximum size
37041      */
37042     setMaximumSize : function(maxSize){
37043         this.maxSize = maxSize;
37044     },
37045     
37046     /**
37047      * Sets the initialize size for the resizing element
37048      * @param {Number} size The initial size
37049      */
37050     setCurrentSize : function(size){
37051         var oldAnimate = this.animate;
37052         this.animate = false;
37053         this.adapter.setElementSize(this, size);
37054         this.animate = oldAnimate;
37055     },
37056     
37057     /**
37058      * Destroy this splitbar. 
37059      * @param {Boolean} removeEl True to remove the element
37060      */
37061     destroy : function(removeEl){
37062         if(this.shim){
37063             this.shim.remove();
37064         }
37065         this.dd.unreg();
37066         this.proxy.parentNode.removeChild(this.proxy);
37067         if(removeEl){
37068             this.el.remove();
37069         }
37070     }
37071 });
37072
37073 /**
37074  * @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.
37075  */
37076 Roo.bootstrap.SplitBar.createProxy = function(dir){
37077     var proxy = new Roo.Element(document.createElement("div"));
37078     proxy.unselectable();
37079     var cls = 'roo-splitbar-proxy';
37080     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37081     document.body.appendChild(proxy.dom);
37082     return proxy.dom;
37083 };
37084
37085 /** 
37086  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37087  * Default Adapter. It assumes the splitter and resizing element are not positioned
37088  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37089  */
37090 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37091 };
37092
37093 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37094     // do nothing for now
37095     init : function(s){
37096     
37097     },
37098     /**
37099      * Called before drag operations to get the current size of the resizing element. 
37100      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37101      */
37102      getElementSize : function(s){
37103         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37104             return s.resizingEl.getWidth();
37105         }else{
37106             return s.resizingEl.getHeight();
37107         }
37108     },
37109     
37110     /**
37111      * Called after drag operations to set the size of the resizing element.
37112      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37113      * @param {Number} newSize The new size to set
37114      * @param {Function} onComplete A function to be invoked when resizing is complete
37115      */
37116     setElementSize : function(s, newSize, onComplete){
37117         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37118             if(!s.animate){
37119                 s.resizingEl.setWidth(newSize);
37120                 if(onComplete){
37121                     onComplete(s, newSize);
37122                 }
37123             }else{
37124                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37125             }
37126         }else{
37127             
37128             if(!s.animate){
37129                 s.resizingEl.setHeight(newSize);
37130                 if(onComplete){
37131                     onComplete(s, newSize);
37132                 }
37133             }else{
37134                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37135             }
37136         }
37137     }
37138 };
37139
37140 /** 
37141  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37142  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37143  * Adapter that  moves the splitter element to align with the resized sizing element. 
37144  * Used with an absolute positioned SplitBar.
37145  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37146  * document.body, make sure you assign an id to the body element.
37147  */
37148 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37149     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37150     this.container = Roo.get(container);
37151 };
37152
37153 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37154     init : function(s){
37155         this.basic.init(s);
37156     },
37157     
37158     getElementSize : function(s){
37159         return this.basic.getElementSize(s);
37160     },
37161     
37162     setElementSize : function(s, newSize, onComplete){
37163         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37164     },
37165     
37166     moveSplitter : function(s){
37167         var yes = Roo.bootstrap.SplitBar;
37168         switch(s.placement){
37169             case yes.LEFT:
37170                 s.el.setX(s.resizingEl.getRight());
37171                 break;
37172             case yes.RIGHT:
37173                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37174                 break;
37175             case yes.TOP:
37176                 s.el.setY(s.resizingEl.getBottom());
37177                 break;
37178             case yes.BOTTOM:
37179                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37180                 break;
37181         }
37182     }
37183 };
37184
37185 /**
37186  * Orientation constant - Create a vertical SplitBar
37187  * @static
37188  * @type Number
37189  */
37190 Roo.bootstrap.SplitBar.VERTICAL = 1;
37191
37192 /**
37193  * Orientation constant - Create a horizontal SplitBar
37194  * @static
37195  * @type Number
37196  */
37197 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37198
37199 /**
37200  * Placement constant - The resizing element is to the left of the splitter element
37201  * @static
37202  * @type Number
37203  */
37204 Roo.bootstrap.SplitBar.LEFT = 1;
37205
37206 /**
37207  * Placement constant - The resizing element is to the right of the splitter element
37208  * @static
37209  * @type Number
37210  */
37211 Roo.bootstrap.SplitBar.RIGHT = 2;
37212
37213 /**
37214  * Placement constant - The resizing element is positioned above the splitter element
37215  * @static
37216  * @type Number
37217  */
37218 Roo.bootstrap.SplitBar.TOP = 3;
37219
37220 /**
37221  * Placement constant - The resizing element is positioned under splitter element
37222  * @static
37223  * @type Number
37224  */
37225 Roo.bootstrap.SplitBar.BOTTOM = 4;
37226 Roo.namespace("Roo.bootstrap.layout");/*
37227  * Based on:
37228  * Ext JS Library 1.1.1
37229  * Copyright(c) 2006-2007, Ext JS, LLC.
37230  *
37231  * Originally Released Under LGPL - original licence link has changed is not relivant.
37232  *
37233  * Fork - LGPL
37234  * <script type="text/javascript">
37235  */
37236
37237 /**
37238  * @class Roo.bootstrap.layout.Manager
37239  * @extends Roo.bootstrap.Component
37240  * Base class for layout managers.
37241  */
37242 Roo.bootstrap.layout.Manager = function(config)
37243 {
37244     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37245
37246
37247
37248
37249
37250     /** false to disable window resize monitoring @type Boolean */
37251     this.monitorWindowResize = true;
37252     this.regions = {};
37253     this.addEvents({
37254         /**
37255          * @event layout
37256          * Fires when a layout is performed.
37257          * @param {Roo.LayoutManager} this
37258          */
37259         "layout" : true,
37260         /**
37261          * @event regionresized
37262          * Fires when the user resizes a region.
37263          * @param {Roo.LayoutRegion} region The resized region
37264          * @param {Number} newSize The new size (width for east/west, height for north/south)
37265          */
37266         "regionresized" : true,
37267         /**
37268          * @event regioncollapsed
37269          * Fires when a region is collapsed.
37270          * @param {Roo.LayoutRegion} region The collapsed region
37271          */
37272         "regioncollapsed" : true,
37273         /**
37274          * @event regionexpanded
37275          * Fires when a region is expanded.
37276          * @param {Roo.LayoutRegion} region The expanded region
37277          */
37278         "regionexpanded" : true
37279     });
37280     this.updating = false;
37281
37282     if (config.el) {
37283         this.el = Roo.get(config.el);
37284         this.initEvents();
37285     }
37286
37287 };
37288
37289 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37290
37291
37292     regions : null,
37293
37294     monitorWindowResize : true,
37295
37296
37297     updating : false,
37298
37299
37300     onRender : function(ct, position)
37301     {
37302         if(!this.el){
37303             this.el = Roo.get(ct);
37304             this.initEvents();
37305         }
37306         //this.fireEvent('render',this);
37307     },
37308
37309
37310     initEvents: function()
37311     {
37312
37313
37314         // ie scrollbar fix
37315         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37316             document.body.scroll = "no";
37317         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37318             this.el.position('relative');
37319         }
37320         this.id = this.el.id;
37321         this.el.addClass("roo-layout-container");
37322         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37323         if(this.el.dom != document.body ) {
37324             this.el.on('resize', this.layout,this);
37325             this.el.on('show', this.layout,this);
37326         }
37327
37328     },
37329
37330     /**
37331      * Returns true if this layout is currently being updated
37332      * @return {Boolean}
37333      */
37334     isUpdating : function(){
37335         return this.updating;
37336     },
37337
37338     /**
37339      * Suspend the LayoutManager from doing auto-layouts while
37340      * making multiple add or remove calls
37341      */
37342     beginUpdate : function(){
37343         this.updating = true;
37344     },
37345
37346     /**
37347      * Restore auto-layouts and optionally disable the manager from performing a layout
37348      * @param {Boolean} noLayout true to disable a layout update
37349      */
37350     endUpdate : function(noLayout){
37351         this.updating = false;
37352         if(!noLayout){
37353             this.layout();
37354         }
37355     },
37356
37357     layout: function(){
37358         // abstract...
37359     },
37360
37361     onRegionResized : function(region, newSize){
37362         this.fireEvent("regionresized", region, newSize);
37363         this.layout();
37364     },
37365
37366     onRegionCollapsed : function(region){
37367         this.fireEvent("regioncollapsed", region);
37368     },
37369
37370     onRegionExpanded : function(region){
37371         this.fireEvent("regionexpanded", region);
37372     },
37373
37374     /**
37375      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37376      * performs box-model adjustments.
37377      * @return {Object} The size as an object {width: (the width), height: (the height)}
37378      */
37379     getViewSize : function()
37380     {
37381         var size;
37382         if(this.el.dom != document.body){
37383             size = this.el.getSize();
37384         }else{
37385             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37386         }
37387         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37388         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37389         return size;
37390     },
37391
37392     /**
37393      * Returns the Element this layout is bound to.
37394      * @return {Roo.Element}
37395      */
37396     getEl : function(){
37397         return this.el;
37398     },
37399
37400     /**
37401      * Returns the specified region.
37402      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37403      * @return {Roo.LayoutRegion}
37404      */
37405     getRegion : function(target){
37406         return this.regions[target.toLowerCase()];
37407     },
37408
37409     onWindowResize : function(){
37410         if(this.monitorWindowResize){
37411             this.layout();
37412         }
37413     }
37414 });
37415 /*
37416  * Based on:
37417  * Ext JS Library 1.1.1
37418  * Copyright(c) 2006-2007, Ext JS, LLC.
37419  *
37420  * Originally Released Under LGPL - original licence link has changed is not relivant.
37421  *
37422  * Fork - LGPL
37423  * <script type="text/javascript">
37424  */
37425 /**
37426  * @class Roo.bootstrap.layout.Border
37427  * @extends Roo.bootstrap.layout.Manager
37428  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37429  * please see: examples/bootstrap/nested.html<br><br>
37430  
37431 <b>The container the layout is rendered into can be either the body element or any other element.
37432 If it is not the body element, the container needs to either be an absolute positioned element,
37433 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37434 the container size if it is not the body element.</b>
37435
37436 * @constructor
37437 * Create a new Border
37438 * @param {Object} config Configuration options
37439  */
37440 Roo.bootstrap.layout.Border = function(config){
37441     config = config || {};
37442     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37443     
37444     
37445     
37446     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37447         if(config[region]){
37448             config[region].region = region;
37449             this.addRegion(config[region]);
37450         }
37451     },this);
37452     
37453 };
37454
37455 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37456
37457 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37458     
37459     parent : false, // this might point to a 'nest' or a ???
37460     
37461     /**
37462      * Creates and adds a new region if it doesn't already exist.
37463      * @param {String} target The target region key (north, south, east, west or center).
37464      * @param {Object} config The regions config object
37465      * @return {BorderLayoutRegion} The new region
37466      */
37467     addRegion : function(config)
37468     {
37469         if(!this.regions[config.region]){
37470             var r = this.factory(config);
37471             this.bindRegion(r);
37472         }
37473         return this.regions[config.region];
37474     },
37475
37476     // private (kinda)
37477     bindRegion : function(r){
37478         this.regions[r.config.region] = r;
37479         
37480         r.on("visibilitychange",    this.layout, this);
37481         r.on("paneladded",          this.layout, this);
37482         r.on("panelremoved",        this.layout, this);
37483         r.on("invalidated",         this.layout, this);
37484         r.on("resized",             this.onRegionResized, this);
37485         r.on("collapsed",           this.onRegionCollapsed, this);
37486         r.on("expanded",            this.onRegionExpanded, this);
37487     },
37488
37489     /**
37490      * Performs a layout update.
37491      */
37492     layout : function()
37493     {
37494         if(this.updating) {
37495             return;
37496         }
37497         
37498         // render all the rebions if they have not been done alreayd?
37499         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37500             if(this.regions[region] && !this.regions[region].bodyEl){
37501                 this.regions[region].onRender(this.el)
37502             }
37503         },this);
37504         
37505         var size = this.getViewSize();
37506         var w = size.width;
37507         var h = size.height;
37508         var centerW = w;
37509         var centerH = h;
37510         var centerY = 0;
37511         var centerX = 0;
37512         //var x = 0, y = 0;
37513
37514         var rs = this.regions;
37515         var north = rs["north"];
37516         var south = rs["south"]; 
37517         var west = rs["west"];
37518         var east = rs["east"];
37519         var center = rs["center"];
37520         //if(this.hideOnLayout){ // not supported anymore
37521             //c.el.setStyle("display", "none");
37522         //}
37523         if(north && north.isVisible()){
37524             var b = north.getBox();
37525             var m = north.getMargins();
37526             b.width = w - (m.left+m.right);
37527             b.x = m.left;
37528             b.y = m.top;
37529             centerY = b.height + b.y + m.bottom;
37530             centerH -= centerY;
37531             north.updateBox(this.safeBox(b));
37532         }
37533         if(south && south.isVisible()){
37534             var b = south.getBox();
37535             var m = south.getMargins();
37536             b.width = w - (m.left+m.right);
37537             b.x = m.left;
37538             var totalHeight = (b.height + m.top + m.bottom);
37539             b.y = h - totalHeight + m.top;
37540             centerH -= totalHeight;
37541             south.updateBox(this.safeBox(b));
37542         }
37543         if(west && west.isVisible()){
37544             var b = west.getBox();
37545             var m = west.getMargins();
37546             b.height = centerH - (m.top+m.bottom);
37547             b.x = m.left;
37548             b.y = centerY + m.top;
37549             var totalWidth = (b.width + m.left + m.right);
37550             centerX += totalWidth;
37551             centerW -= totalWidth;
37552             west.updateBox(this.safeBox(b));
37553         }
37554         if(east && east.isVisible()){
37555             var b = east.getBox();
37556             var m = east.getMargins();
37557             b.height = centerH - (m.top+m.bottom);
37558             var totalWidth = (b.width + m.left + m.right);
37559             b.x = w - totalWidth + m.left;
37560             b.y = centerY + m.top;
37561             centerW -= totalWidth;
37562             east.updateBox(this.safeBox(b));
37563         }
37564         if(center){
37565             var m = center.getMargins();
37566             var centerBox = {
37567                 x: centerX + m.left,
37568                 y: centerY + m.top,
37569                 width: centerW - (m.left+m.right),
37570                 height: centerH - (m.top+m.bottom)
37571             };
37572             //if(this.hideOnLayout){
37573                 //center.el.setStyle("display", "block");
37574             //}
37575             center.updateBox(this.safeBox(centerBox));
37576         }
37577         this.el.repaint();
37578         this.fireEvent("layout", this);
37579     },
37580
37581     // private
37582     safeBox : function(box){
37583         box.width = Math.max(0, box.width);
37584         box.height = Math.max(0, box.height);
37585         return box;
37586     },
37587
37588     /**
37589      * Adds a ContentPanel (or subclass) to this layout.
37590      * @param {String} target The target region key (north, south, east, west or center).
37591      * @param {Roo.ContentPanel} panel The panel to add
37592      * @return {Roo.ContentPanel} The added panel
37593      */
37594     add : function(target, panel){
37595          
37596         target = target.toLowerCase();
37597         return this.regions[target].add(panel);
37598     },
37599
37600     /**
37601      * Remove a ContentPanel (or subclass) to this layout.
37602      * @param {String} target The target region key (north, south, east, west or center).
37603      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37604      * @return {Roo.ContentPanel} The removed panel
37605      */
37606     remove : function(target, panel){
37607         target = target.toLowerCase();
37608         return this.regions[target].remove(panel);
37609     },
37610
37611     /**
37612      * Searches all regions for a panel with the specified id
37613      * @param {String} panelId
37614      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37615      */
37616     findPanel : function(panelId){
37617         var rs = this.regions;
37618         for(var target in rs){
37619             if(typeof rs[target] != "function"){
37620                 var p = rs[target].getPanel(panelId);
37621                 if(p){
37622                     return p;
37623                 }
37624             }
37625         }
37626         return null;
37627     },
37628
37629     /**
37630      * Searches all regions for a panel with the specified id and activates (shows) it.
37631      * @param {String/ContentPanel} panelId The panels id or the panel itself
37632      * @return {Roo.ContentPanel} The shown panel or null
37633      */
37634     showPanel : function(panelId) {
37635       var rs = this.regions;
37636       for(var target in rs){
37637          var r = rs[target];
37638          if(typeof r != "function"){
37639             if(r.hasPanel(panelId)){
37640                return r.showPanel(panelId);
37641             }
37642          }
37643       }
37644       return null;
37645    },
37646
37647    /**
37648      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37649      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37650      */
37651    /*
37652     restoreState : function(provider){
37653         if(!provider){
37654             provider = Roo.state.Manager;
37655         }
37656         var sm = new Roo.LayoutStateManager();
37657         sm.init(this, provider);
37658     },
37659 */
37660  
37661  
37662     /**
37663      * Adds a xtype elements to the layout.
37664      * <pre><code>
37665
37666 layout.addxtype({
37667        xtype : 'ContentPanel',
37668        region: 'west',
37669        items: [ .... ]
37670    }
37671 );
37672
37673 layout.addxtype({
37674         xtype : 'NestedLayoutPanel',
37675         region: 'west',
37676         layout: {
37677            center: { },
37678            west: { }   
37679         },
37680         items : [ ... list of content panels or nested layout panels.. ]
37681    }
37682 );
37683 </code></pre>
37684      * @param {Object} cfg Xtype definition of item to add.
37685      */
37686     addxtype : function(cfg)
37687     {
37688         // basically accepts a pannel...
37689         // can accept a layout region..!?!?
37690         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37691         
37692         
37693         // theory?  children can only be panels??
37694         
37695         //if (!cfg.xtype.match(/Panel$/)) {
37696         //    return false;
37697         //}
37698         var ret = false;
37699         
37700         if (typeof(cfg.region) == 'undefined') {
37701             Roo.log("Failed to add Panel, region was not set");
37702             Roo.log(cfg);
37703             return false;
37704         }
37705         var region = cfg.region;
37706         delete cfg.region;
37707         
37708           
37709         var xitems = [];
37710         if (cfg.items) {
37711             xitems = cfg.items;
37712             delete cfg.items;
37713         }
37714         var nb = false;
37715         
37716         if ( region == 'center') {
37717             Roo.log("Center: " + cfg.title);
37718         }
37719         
37720         
37721         switch(cfg.xtype) 
37722         {
37723             case 'Content':  // ContentPanel (el, cfg)
37724             case 'Scroll':  // ContentPanel (el, cfg)
37725             case 'View': 
37726                 cfg.autoCreate = cfg.autoCreate || true;
37727                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37728                 //} else {
37729                 //    var el = this.el.createChild();
37730                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37731                 //}
37732                 
37733                 this.add(region, ret);
37734                 break;
37735             
37736             /*
37737             case 'TreePanel': // our new panel!
37738                 cfg.el = this.el.createChild();
37739                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37740                 this.add(region, ret);
37741                 break;
37742             */
37743             
37744             case 'Nest': 
37745                 // create a new Layout (which is  a Border Layout...
37746                 
37747                 var clayout = cfg.layout;
37748                 clayout.el  = this.el.createChild();
37749                 clayout.items   = clayout.items  || [];
37750                 
37751                 delete cfg.layout;
37752                 
37753                 // replace this exitems with the clayout ones..
37754                 xitems = clayout.items;
37755                  
37756                 // force background off if it's in center...
37757                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37758                     cfg.background = false;
37759                 }
37760                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37761                 
37762                 
37763                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37764                 //console.log('adding nested layout panel '  + cfg.toSource());
37765                 this.add(region, ret);
37766                 nb = {}; /// find first...
37767                 break;
37768             
37769             case 'Grid':
37770                 
37771                 // needs grid and region
37772                 
37773                 //var el = this.getRegion(region).el.createChild();
37774                 /*
37775                  *var el = this.el.createChild();
37776                 // create the grid first...
37777                 cfg.grid.container = el;
37778                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37779                 */
37780                 
37781                 if (region == 'center' && this.active ) {
37782                     cfg.background = false;
37783                 }
37784                 
37785                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37786                 
37787                 this.add(region, ret);
37788                 /*
37789                 if (cfg.background) {
37790                     // render grid on panel activation (if panel background)
37791                     ret.on('activate', function(gp) {
37792                         if (!gp.grid.rendered) {
37793                     //        gp.grid.render(el);
37794                         }
37795                     });
37796                 } else {
37797                   //  cfg.grid.render(el);
37798                 }
37799                 */
37800                 break;
37801            
37802            
37803             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37804                 // it was the old xcomponent building that caused this before.
37805                 // espeically if border is the top element in the tree.
37806                 ret = this;
37807                 break; 
37808                 
37809                     
37810                 
37811                 
37812                 
37813             default:
37814                 /*
37815                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37816                     
37817                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37818                     this.add(region, ret);
37819                 } else {
37820                 */
37821                     Roo.log(cfg);
37822                     throw "Can not add '" + cfg.xtype + "' to Border";
37823                     return null;
37824              
37825                                 
37826              
37827         }
37828         this.beginUpdate();
37829         // add children..
37830         var region = '';
37831         var abn = {};
37832         Roo.each(xitems, function(i)  {
37833             region = nb && i.region ? i.region : false;
37834             
37835             var add = ret.addxtype(i);
37836            
37837             if (region) {
37838                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37839                 if (!i.background) {
37840                     abn[region] = nb[region] ;
37841                 }
37842             }
37843             
37844         });
37845         this.endUpdate();
37846
37847         // make the last non-background panel active..
37848         //if (nb) { Roo.log(abn); }
37849         if (nb) {
37850             
37851             for(var r in abn) {
37852                 region = this.getRegion(r);
37853                 if (region) {
37854                     // tried using nb[r], but it does not work..
37855                      
37856                     region.showPanel(abn[r]);
37857                    
37858                 }
37859             }
37860         }
37861         return ret;
37862         
37863     },
37864     
37865     
37866 // private
37867     factory : function(cfg)
37868     {
37869         
37870         var validRegions = Roo.bootstrap.layout.Border.regions;
37871
37872         var target = cfg.region;
37873         cfg.mgr = this;
37874         
37875         var r = Roo.bootstrap.layout;
37876         Roo.log(target);
37877         switch(target){
37878             case "north":
37879                 return new r.North(cfg);
37880             case "south":
37881                 return new r.South(cfg);
37882             case "east":
37883                 return new r.East(cfg);
37884             case "west":
37885                 return new r.West(cfg);
37886             case "center":
37887                 return new r.Center(cfg);
37888         }
37889         throw 'Layout region "'+target+'" not supported.';
37890     }
37891     
37892     
37893 });
37894  /*
37895  * Based on:
37896  * Ext JS Library 1.1.1
37897  * Copyright(c) 2006-2007, Ext JS, LLC.
37898  *
37899  * Originally Released Under LGPL - original licence link has changed is not relivant.
37900  *
37901  * Fork - LGPL
37902  * <script type="text/javascript">
37903  */
37904  
37905 /**
37906  * @class Roo.bootstrap.layout.Basic
37907  * @extends Roo.util.Observable
37908  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37909  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37910  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37911  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37912  * @cfg {string}   region  the region that it inhabits..
37913  * @cfg {bool}   skipConfig skip config?
37914  * 
37915
37916  */
37917 Roo.bootstrap.layout.Basic = function(config){
37918     
37919     this.mgr = config.mgr;
37920     
37921     this.position = config.region;
37922     
37923     var skipConfig = config.skipConfig;
37924     
37925     this.events = {
37926         /**
37927          * @scope Roo.BasicLayoutRegion
37928          */
37929         
37930         /**
37931          * @event beforeremove
37932          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37933          * @param {Roo.LayoutRegion} this
37934          * @param {Roo.ContentPanel} panel The panel
37935          * @param {Object} e The cancel event object
37936          */
37937         "beforeremove" : true,
37938         /**
37939          * @event invalidated
37940          * Fires when the layout for this region is changed.
37941          * @param {Roo.LayoutRegion} this
37942          */
37943         "invalidated" : true,
37944         /**
37945          * @event visibilitychange
37946          * Fires when this region is shown or hidden 
37947          * @param {Roo.LayoutRegion} this
37948          * @param {Boolean} visibility true or false
37949          */
37950         "visibilitychange" : true,
37951         /**
37952          * @event paneladded
37953          * Fires when a panel is added. 
37954          * @param {Roo.LayoutRegion} this
37955          * @param {Roo.ContentPanel} panel The panel
37956          */
37957         "paneladded" : true,
37958         /**
37959          * @event panelremoved
37960          * Fires when a panel is removed. 
37961          * @param {Roo.LayoutRegion} this
37962          * @param {Roo.ContentPanel} panel The panel
37963          */
37964         "panelremoved" : true,
37965         /**
37966          * @event beforecollapse
37967          * Fires when this region before collapse.
37968          * @param {Roo.LayoutRegion} this
37969          */
37970         "beforecollapse" : true,
37971         /**
37972          * @event collapsed
37973          * Fires when this region is collapsed.
37974          * @param {Roo.LayoutRegion} this
37975          */
37976         "collapsed" : true,
37977         /**
37978          * @event expanded
37979          * Fires when this region is expanded.
37980          * @param {Roo.LayoutRegion} this
37981          */
37982         "expanded" : true,
37983         /**
37984          * @event slideshow
37985          * Fires when this region is slid into view.
37986          * @param {Roo.LayoutRegion} this
37987          */
37988         "slideshow" : true,
37989         /**
37990          * @event slidehide
37991          * Fires when this region slides out of view. 
37992          * @param {Roo.LayoutRegion} this
37993          */
37994         "slidehide" : true,
37995         /**
37996          * @event panelactivated
37997          * Fires when a panel is activated. 
37998          * @param {Roo.LayoutRegion} this
37999          * @param {Roo.ContentPanel} panel The activated panel
38000          */
38001         "panelactivated" : true,
38002         /**
38003          * @event resized
38004          * Fires when the user resizes this region. 
38005          * @param {Roo.LayoutRegion} this
38006          * @param {Number} newSize The new size (width for east/west, height for north/south)
38007          */
38008         "resized" : true
38009     };
38010     /** A collection of panels in this region. @type Roo.util.MixedCollection */
38011     this.panels = new Roo.util.MixedCollection();
38012     this.panels.getKey = this.getPanelId.createDelegate(this);
38013     this.box = null;
38014     this.activePanel = null;
38015     // ensure listeners are added...
38016     
38017     if (config.listeners || config.events) {
38018         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
38019             listeners : config.listeners || {},
38020             events : config.events || {}
38021         });
38022     }
38023     
38024     if(skipConfig !== true){
38025         this.applyConfig(config);
38026     }
38027 };
38028
38029 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38030 {
38031     getPanelId : function(p){
38032         return p.getId();
38033     },
38034     
38035     applyConfig : function(config){
38036         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38037         this.config = config;
38038         
38039     },
38040     
38041     /**
38042      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38043      * the width, for horizontal (north, south) the height.
38044      * @param {Number} newSize The new width or height
38045      */
38046     resizeTo : function(newSize){
38047         var el = this.el ? this.el :
38048                  (this.activePanel ? this.activePanel.getEl() : null);
38049         if(el){
38050             switch(this.position){
38051                 case "east":
38052                 case "west":
38053                     el.setWidth(newSize);
38054                     this.fireEvent("resized", this, newSize);
38055                 break;
38056                 case "north":
38057                 case "south":
38058                     el.setHeight(newSize);
38059                     this.fireEvent("resized", this, newSize);
38060                 break;                
38061             }
38062         }
38063     },
38064     
38065     getBox : function(){
38066         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38067     },
38068     
38069     getMargins : function(){
38070         return this.margins;
38071     },
38072     
38073     updateBox : function(box){
38074         this.box = box;
38075         var el = this.activePanel.getEl();
38076         el.dom.style.left = box.x + "px";
38077         el.dom.style.top = box.y + "px";
38078         this.activePanel.setSize(box.width, box.height);
38079     },
38080     
38081     /**
38082      * Returns the container element for this region.
38083      * @return {Roo.Element}
38084      */
38085     getEl : function(){
38086         return this.activePanel;
38087     },
38088     
38089     /**
38090      * Returns true if this region is currently visible.
38091      * @return {Boolean}
38092      */
38093     isVisible : function(){
38094         return this.activePanel ? true : false;
38095     },
38096     
38097     setActivePanel : function(panel){
38098         panel = this.getPanel(panel);
38099         if(this.activePanel && this.activePanel != panel){
38100             this.activePanel.setActiveState(false);
38101             this.activePanel.getEl().setLeftTop(-10000,-10000);
38102         }
38103         this.activePanel = panel;
38104         panel.setActiveState(true);
38105         if(this.box){
38106             panel.setSize(this.box.width, this.box.height);
38107         }
38108         this.fireEvent("panelactivated", this, panel);
38109         this.fireEvent("invalidated");
38110     },
38111     
38112     /**
38113      * Show the specified panel.
38114      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38115      * @return {Roo.ContentPanel} The shown panel or null
38116      */
38117     showPanel : function(panel){
38118         panel = this.getPanel(panel);
38119         if(panel){
38120             this.setActivePanel(panel);
38121         }
38122         return panel;
38123     },
38124     
38125     /**
38126      * Get the active panel for this region.
38127      * @return {Roo.ContentPanel} The active panel or null
38128      */
38129     getActivePanel : function(){
38130         return this.activePanel;
38131     },
38132     
38133     /**
38134      * Add the passed ContentPanel(s)
38135      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38136      * @return {Roo.ContentPanel} The panel added (if only one was added)
38137      */
38138     add : function(panel){
38139         if(arguments.length > 1){
38140             for(var i = 0, len = arguments.length; i < len; i++) {
38141                 this.add(arguments[i]);
38142             }
38143             return null;
38144         }
38145         if(this.hasPanel(panel)){
38146             this.showPanel(panel);
38147             return panel;
38148         }
38149         var el = panel.getEl();
38150         if(el.dom.parentNode != this.mgr.el.dom){
38151             this.mgr.el.dom.appendChild(el.dom);
38152         }
38153         if(panel.setRegion){
38154             panel.setRegion(this);
38155         }
38156         this.panels.add(panel);
38157         el.setStyle("position", "absolute");
38158         if(!panel.background){
38159             this.setActivePanel(panel);
38160             if(this.config.initialSize && this.panels.getCount()==1){
38161                 this.resizeTo(this.config.initialSize);
38162             }
38163         }
38164         this.fireEvent("paneladded", this, panel);
38165         return panel;
38166     },
38167     
38168     /**
38169      * Returns true if the panel is in this region.
38170      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38171      * @return {Boolean}
38172      */
38173     hasPanel : function(panel){
38174         if(typeof panel == "object"){ // must be panel obj
38175             panel = panel.getId();
38176         }
38177         return this.getPanel(panel) ? true : false;
38178     },
38179     
38180     /**
38181      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38182      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38183      * @param {Boolean} preservePanel Overrides the config preservePanel option
38184      * @return {Roo.ContentPanel} The panel that was removed
38185      */
38186     remove : function(panel, preservePanel){
38187         panel = this.getPanel(panel);
38188         if(!panel){
38189             return null;
38190         }
38191         var e = {};
38192         this.fireEvent("beforeremove", this, panel, e);
38193         if(e.cancel === true){
38194             return null;
38195         }
38196         var panelId = panel.getId();
38197         this.panels.removeKey(panelId);
38198         return panel;
38199     },
38200     
38201     /**
38202      * Returns the panel specified or null if it's not in this region.
38203      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38204      * @return {Roo.ContentPanel}
38205      */
38206     getPanel : function(id){
38207         if(typeof id == "object"){ // must be panel obj
38208             return id;
38209         }
38210         return this.panels.get(id);
38211     },
38212     
38213     /**
38214      * Returns this regions position (north/south/east/west/center).
38215      * @return {String} 
38216      */
38217     getPosition: function(){
38218         return this.position;    
38219     }
38220 });/*
38221  * Based on:
38222  * Ext JS Library 1.1.1
38223  * Copyright(c) 2006-2007, Ext JS, LLC.
38224  *
38225  * Originally Released Under LGPL - original licence link has changed is not relivant.
38226  *
38227  * Fork - LGPL
38228  * <script type="text/javascript">
38229  */
38230  
38231 /**
38232  * @class Roo.bootstrap.layout.Region
38233  * @extends Roo.bootstrap.layout.Basic
38234  * This class represents a region in a layout manager.
38235  
38236  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38237  * @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})
38238  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38239  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38240  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38241  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38242  * @cfg {String}    title           The title for the region (overrides panel titles)
38243  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38244  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38245  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38246  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38247  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38248  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38249  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38250  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38251  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38252  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38253
38254  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38255  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38256  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38257  * @cfg {Number}    width           For East/West panels
38258  * @cfg {Number}    height          For North/South panels
38259  * @cfg {Boolean}   split           To show the splitter
38260  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38261  * 
38262  * @cfg {string}   cls             Extra CSS classes to add to region
38263  * 
38264  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38265  * @cfg {string}   region  the region that it inhabits..
38266  *
38267
38268  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38269  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38270
38271  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38272  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38273  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38274  */
38275 Roo.bootstrap.layout.Region = function(config)
38276 {
38277     this.applyConfig(config);
38278
38279     var mgr = config.mgr;
38280     var pos = config.region;
38281     config.skipConfig = true;
38282     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38283     
38284     if (mgr.el) {
38285         this.onRender(mgr.el);   
38286     }
38287      
38288     this.visible = true;
38289     this.collapsed = false;
38290     this.unrendered_panels = [];
38291 };
38292
38293 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38294
38295     position: '', // set by wrapper (eg. north/south etc..)
38296     unrendered_panels : null,  // unrendered panels.
38297     
38298     tabPosition : false,
38299     
38300     mgr: false, // points to 'Border'
38301     
38302     
38303     createBody : function(){
38304         /** This region's body element 
38305         * @type Roo.Element */
38306         this.bodyEl = this.el.createChild({
38307                 tag: "div",
38308                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38309         });
38310     },
38311
38312     onRender: function(ctr, pos)
38313     {
38314         var dh = Roo.DomHelper;
38315         /** This region's container element 
38316         * @type Roo.Element */
38317         this.el = dh.append(ctr.dom, {
38318                 tag: "div",
38319                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38320             }, true);
38321         /** This region's title element 
38322         * @type Roo.Element */
38323     
38324         this.titleEl = dh.append(this.el.dom,  {
38325                 tag: "div",
38326                 unselectable: "on",
38327                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38328                 children:[
38329                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38330                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38331                 ]
38332             }, true);
38333         
38334         this.titleEl.enableDisplayMode();
38335         /** This region's title text element 
38336         * @type HTMLElement */
38337         this.titleTextEl = this.titleEl.dom.firstChild;
38338         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38339         /*
38340         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38341         this.closeBtn.enableDisplayMode();
38342         this.closeBtn.on("click", this.closeClicked, this);
38343         this.closeBtn.hide();
38344     */
38345         this.createBody(this.config);
38346         if(this.config.hideWhenEmpty){
38347             this.hide();
38348             this.on("paneladded", this.validateVisibility, this);
38349             this.on("panelremoved", this.validateVisibility, this);
38350         }
38351         if(this.autoScroll){
38352             this.bodyEl.setStyle("overflow", "auto");
38353         }else{
38354             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38355         }
38356         //if(c.titlebar !== false){
38357             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38358                 this.titleEl.hide();
38359             }else{
38360                 this.titleEl.show();
38361                 if(this.config.title){
38362                     this.titleTextEl.innerHTML = this.config.title;
38363                 }
38364             }
38365         //}
38366         if(this.config.collapsed){
38367             this.collapse(true);
38368         }
38369         if(this.config.hidden){
38370             this.hide();
38371         }
38372         
38373         if (this.unrendered_panels && this.unrendered_panels.length) {
38374             for (var i =0;i< this.unrendered_panels.length; i++) {
38375                 this.add(this.unrendered_panels[i]);
38376             }
38377             this.unrendered_panels = null;
38378             
38379         }
38380         
38381     },
38382     
38383     applyConfig : function(c)
38384     {
38385         /*
38386          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38387             var dh = Roo.DomHelper;
38388             if(c.titlebar !== false){
38389                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38390                 this.collapseBtn.on("click", this.collapse, this);
38391                 this.collapseBtn.enableDisplayMode();
38392                 /*
38393                 if(c.showPin === true || this.showPin){
38394                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38395                     this.stickBtn.enableDisplayMode();
38396                     this.stickBtn.on("click", this.expand, this);
38397                     this.stickBtn.hide();
38398                 }
38399                 
38400             }
38401             */
38402             /** This region's collapsed element
38403             * @type Roo.Element */
38404             /*
38405              *
38406             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38407                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38408             ]}, true);
38409             
38410             if(c.floatable !== false){
38411                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38412                this.collapsedEl.on("click", this.collapseClick, this);
38413             }
38414
38415             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38416                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38417                    id: "message", unselectable: "on", style:{"float":"left"}});
38418                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38419              }
38420             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38421             this.expandBtn.on("click", this.expand, this);
38422             
38423         }
38424         
38425         if(this.collapseBtn){
38426             this.collapseBtn.setVisible(c.collapsible == true);
38427         }
38428         
38429         this.cmargins = c.cmargins || this.cmargins ||
38430                          (this.position == "west" || this.position == "east" ?
38431                              {top: 0, left: 2, right:2, bottom: 0} :
38432                              {top: 2, left: 0, right:0, bottom: 2});
38433         */
38434         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38435         
38436         
38437         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38438         
38439         this.autoScroll = c.autoScroll || false;
38440         
38441         
38442        
38443         
38444         this.duration = c.duration || .30;
38445         this.slideDuration = c.slideDuration || .45;
38446         this.config = c;
38447        
38448     },
38449     /**
38450      * Returns true if this region is currently visible.
38451      * @return {Boolean}
38452      */
38453     isVisible : function(){
38454         return this.visible;
38455     },
38456
38457     /**
38458      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38459      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38460      */
38461     //setCollapsedTitle : function(title){
38462     //    title = title || "&#160;";
38463      //   if(this.collapsedTitleTextEl){
38464       //      this.collapsedTitleTextEl.innerHTML = title;
38465        // }
38466     //},
38467
38468     getBox : function(){
38469         var b;
38470       //  if(!this.collapsed){
38471             b = this.el.getBox(false, true);
38472        // }else{
38473           //  b = this.collapsedEl.getBox(false, true);
38474         //}
38475         return b;
38476     },
38477
38478     getMargins : function(){
38479         return this.margins;
38480         //return this.collapsed ? this.cmargins : this.margins;
38481     },
38482 /*
38483     highlight : function(){
38484         this.el.addClass("x-layout-panel-dragover");
38485     },
38486
38487     unhighlight : function(){
38488         this.el.removeClass("x-layout-panel-dragover");
38489     },
38490 */
38491     updateBox : function(box)
38492     {
38493         if (!this.bodyEl) {
38494             return; // not rendered yet..
38495         }
38496         
38497         this.box = box;
38498         if(!this.collapsed){
38499             this.el.dom.style.left = box.x + "px";
38500             this.el.dom.style.top = box.y + "px";
38501             this.updateBody(box.width, box.height);
38502         }else{
38503             this.collapsedEl.dom.style.left = box.x + "px";
38504             this.collapsedEl.dom.style.top = box.y + "px";
38505             this.collapsedEl.setSize(box.width, box.height);
38506         }
38507         if(this.tabs){
38508             this.tabs.autoSizeTabs();
38509         }
38510     },
38511
38512     updateBody : function(w, h)
38513     {
38514         if(w !== null){
38515             this.el.setWidth(w);
38516             w -= this.el.getBorderWidth("rl");
38517             if(this.config.adjustments){
38518                 w += this.config.adjustments[0];
38519             }
38520         }
38521         if(h !== null && h > 0){
38522             this.el.setHeight(h);
38523             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38524             h -= this.el.getBorderWidth("tb");
38525             if(this.config.adjustments){
38526                 h += this.config.adjustments[1];
38527             }
38528             this.bodyEl.setHeight(h);
38529             if(this.tabs){
38530                 h = this.tabs.syncHeight(h);
38531             }
38532         }
38533         if(this.panelSize){
38534             w = w !== null ? w : this.panelSize.width;
38535             h = h !== null ? h : this.panelSize.height;
38536         }
38537         if(this.activePanel){
38538             var el = this.activePanel.getEl();
38539             w = w !== null ? w : el.getWidth();
38540             h = h !== null ? h : el.getHeight();
38541             this.panelSize = {width: w, height: h};
38542             this.activePanel.setSize(w, h);
38543         }
38544         if(Roo.isIE && this.tabs){
38545             this.tabs.el.repaint();
38546         }
38547     },
38548
38549     /**
38550      * Returns the container element for this region.
38551      * @return {Roo.Element}
38552      */
38553     getEl : function(){
38554         return this.el;
38555     },
38556
38557     /**
38558      * Hides this region.
38559      */
38560     hide : function(){
38561         //if(!this.collapsed){
38562             this.el.dom.style.left = "-2000px";
38563             this.el.hide();
38564         //}else{
38565          //   this.collapsedEl.dom.style.left = "-2000px";
38566          //   this.collapsedEl.hide();
38567        // }
38568         this.visible = false;
38569         this.fireEvent("visibilitychange", this, false);
38570     },
38571
38572     /**
38573      * Shows this region if it was previously hidden.
38574      */
38575     show : function(){
38576         //if(!this.collapsed){
38577             this.el.show();
38578         //}else{
38579         //    this.collapsedEl.show();
38580        // }
38581         this.visible = true;
38582         this.fireEvent("visibilitychange", this, true);
38583     },
38584 /*
38585     closeClicked : function(){
38586         if(this.activePanel){
38587             this.remove(this.activePanel);
38588         }
38589     },
38590
38591     collapseClick : function(e){
38592         if(this.isSlid){
38593            e.stopPropagation();
38594            this.slideIn();
38595         }else{
38596            e.stopPropagation();
38597            this.slideOut();
38598         }
38599     },
38600 */
38601     /**
38602      * Collapses this region.
38603      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38604      */
38605     /*
38606     collapse : function(skipAnim, skipCheck = false){
38607         if(this.collapsed) {
38608             return;
38609         }
38610         
38611         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38612             
38613             this.collapsed = true;
38614             if(this.split){
38615                 this.split.el.hide();
38616             }
38617             if(this.config.animate && skipAnim !== true){
38618                 this.fireEvent("invalidated", this);
38619                 this.animateCollapse();
38620             }else{
38621                 this.el.setLocation(-20000,-20000);
38622                 this.el.hide();
38623                 this.collapsedEl.show();
38624                 this.fireEvent("collapsed", this);
38625                 this.fireEvent("invalidated", this);
38626             }
38627         }
38628         
38629     },
38630 */
38631     animateCollapse : function(){
38632         // overridden
38633     },
38634
38635     /**
38636      * Expands this region if it was previously collapsed.
38637      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38638      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38639      */
38640     /*
38641     expand : function(e, skipAnim){
38642         if(e) {
38643             e.stopPropagation();
38644         }
38645         if(!this.collapsed || this.el.hasActiveFx()) {
38646             return;
38647         }
38648         if(this.isSlid){
38649             this.afterSlideIn();
38650             skipAnim = true;
38651         }
38652         this.collapsed = false;
38653         if(this.config.animate && skipAnim !== true){
38654             this.animateExpand();
38655         }else{
38656             this.el.show();
38657             if(this.split){
38658                 this.split.el.show();
38659             }
38660             this.collapsedEl.setLocation(-2000,-2000);
38661             this.collapsedEl.hide();
38662             this.fireEvent("invalidated", this);
38663             this.fireEvent("expanded", this);
38664         }
38665     },
38666 */
38667     animateExpand : function(){
38668         // overridden
38669     },
38670
38671     initTabs : function()
38672     {
38673         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38674         
38675         var ts = new Roo.bootstrap.panel.Tabs({
38676             el: this.bodyEl.dom,
38677             region : this,
38678             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38679             disableTooltips: this.config.disableTabTips,
38680             toolbar : this.config.toolbar
38681         });
38682         
38683         if(this.config.hideTabs){
38684             ts.stripWrap.setDisplayed(false);
38685         }
38686         this.tabs = ts;
38687         ts.resizeTabs = this.config.resizeTabs === true;
38688         ts.minTabWidth = this.config.minTabWidth || 40;
38689         ts.maxTabWidth = this.config.maxTabWidth || 250;
38690         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38691         ts.monitorResize = false;
38692         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38693         ts.bodyEl.addClass('roo-layout-tabs-body');
38694         this.panels.each(this.initPanelAsTab, this);
38695     },
38696
38697     initPanelAsTab : function(panel){
38698         var ti = this.tabs.addTab(
38699             panel.getEl().id,
38700             panel.getTitle(),
38701             null,
38702             this.config.closeOnTab && panel.isClosable(),
38703             panel.tpl
38704         );
38705         if(panel.tabTip !== undefined){
38706             ti.setTooltip(panel.tabTip);
38707         }
38708         ti.on("activate", function(){
38709               this.setActivePanel(panel);
38710         }, this);
38711         
38712         if(this.config.closeOnTab){
38713             ti.on("beforeclose", function(t, e){
38714                 e.cancel = true;
38715                 this.remove(panel);
38716             }, this);
38717         }
38718         
38719         panel.tabItem = ti;
38720         
38721         return ti;
38722     },
38723
38724     updatePanelTitle : function(panel, title)
38725     {
38726         if(this.activePanel == panel){
38727             this.updateTitle(title);
38728         }
38729         if(this.tabs){
38730             var ti = this.tabs.getTab(panel.getEl().id);
38731             ti.setText(title);
38732             if(panel.tabTip !== undefined){
38733                 ti.setTooltip(panel.tabTip);
38734             }
38735         }
38736     },
38737
38738     updateTitle : function(title){
38739         if(this.titleTextEl && !this.config.title){
38740             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38741         }
38742     },
38743
38744     setActivePanel : function(panel)
38745     {
38746         panel = this.getPanel(panel);
38747         if(this.activePanel && this.activePanel != panel){
38748             if(this.activePanel.setActiveState(false) === false){
38749                 return;
38750             }
38751         }
38752         this.activePanel = panel;
38753         panel.setActiveState(true);
38754         if(this.panelSize){
38755             panel.setSize(this.panelSize.width, this.panelSize.height);
38756         }
38757         if(this.closeBtn){
38758             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38759         }
38760         this.updateTitle(panel.getTitle());
38761         if(this.tabs){
38762             this.fireEvent("invalidated", this);
38763         }
38764         this.fireEvent("panelactivated", this, panel);
38765     },
38766
38767     /**
38768      * Shows the specified panel.
38769      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38770      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38771      */
38772     showPanel : function(panel)
38773     {
38774         panel = this.getPanel(panel);
38775         if(panel){
38776             if(this.tabs){
38777                 var tab = this.tabs.getTab(panel.getEl().id);
38778                 if(tab.isHidden()){
38779                     this.tabs.unhideTab(tab.id);
38780                 }
38781                 tab.activate();
38782             }else{
38783                 this.setActivePanel(panel);
38784             }
38785         }
38786         return panel;
38787     },
38788
38789     /**
38790      * Get the active panel for this region.
38791      * @return {Roo.ContentPanel} The active panel or null
38792      */
38793     getActivePanel : function(){
38794         return this.activePanel;
38795     },
38796
38797     validateVisibility : function(){
38798         if(this.panels.getCount() < 1){
38799             this.updateTitle("&#160;");
38800             this.closeBtn.hide();
38801             this.hide();
38802         }else{
38803             if(!this.isVisible()){
38804                 this.show();
38805             }
38806         }
38807     },
38808
38809     /**
38810      * Adds the passed ContentPanel(s) to this region.
38811      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38812      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38813      */
38814     add : function(panel)
38815     {
38816         if(arguments.length > 1){
38817             for(var i = 0, len = arguments.length; i < len; i++) {
38818                 this.add(arguments[i]);
38819             }
38820             return null;
38821         }
38822         
38823         // if we have not been rendered yet, then we can not really do much of this..
38824         if (!this.bodyEl) {
38825             this.unrendered_panels.push(panel);
38826             return panel;
38827         }
38828         
38829         
38830         
38831         
38832         if(this.hasPanel(panel)){
38833             this.showPanel(panel);
38834             return panel;
38835         }
38836         panel.setRegion(this);
38837         this.panels.add(panel);
38838        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38839             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38840             // and hide them... ???
38841             this.bodyEl.dom.appendChild(panel.getEl().dom);
38842             if(panel.background !== true){
38843                 this.setActivePanel(panel);
38844             }
38845             this.fireEvent("paneladded", this, panel);
38846             return panel;
38847         }
38848         */
38849         if(!this.tabs){
38850             this.initTabs();
38851         }else{
38852             this.initPanelAsTab(panel);
38853         }
38854         
38855         
38856         if(panel.background !== true){
38857             this.tabs.activate(panel.getEl().id);
38858         }
38859         this.fireEvent("paneladded", this, panel);
38860         return panel;
38861     },
38862
38863     /**
38864      * Hides the tab for the specified panel.
38865      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38866      */
38867     hidePanel : function(panel){
38868         if(this.tabs && (panel = this.getPanel(panel))){
38869             this.tabs.hideTab(panel.getEl().id);
38870         }
38871     },
38872
38873     /**
38874      * Unhides the tab for a previously hidden panel.
38875      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38876      */
38877     unhidePanel : function(panel){
38878         if(this.tabs && (panel = this.getPanel(panel))){
38879             this.tabs.unhideTab(panel.getEl().id);
38880         }
38881     },
38882
38883     clearPanels : function(){
38884         while(this.panels.getCount() > 0){
38885              this.remove(this.panels.first());
38886         }
38887     },
38888
38889     /**
38890      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38891      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38892      * @param {Boolean} preservePanel Overrides the config preservePanel option
38893      * @return {Roo.ContentPanel} The panel that was removed
38894      */
38895     remove : function(panel, preservePanel)
38896     {
38897         panel = this.getPanel(panel);
38898         if(!panel){
38899             return null;
38900         }
38901         var e = {};
38902         this.fireEvent("beforeremove", this, panel, e);
38903         if(e.cancel === true){
38904             return null;
38905         }
38906         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38907         var panelId = panel.getId();
38908         this.panels.removeKey(panelId);
38909         if(preservePanel){
38910             document.body.appendChild(panel.getEl().dom);
38911         }
38912         if(this.tabs){
38913             this.tabs.removeTab(panel.getEl().id);
38914         }else if (!preservePanel){
38915             this.bodyEl.dom.removeChild(panel.getEl().dom);
38916         }
38917         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38918             var p = this.panels.first();
38919             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38920             tempEl.appendChild(p.getEl().dom);
38921             this.bodyEl.update("");
38922             this.bodyEl.dom.appendChild(p.getEl().dom);
38923             tempEl = null;
38924             this.updateTitle(p.getTitle());
38925             this.tabs = null;
38926             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38927             this.setActivePanel(p);
38928         }
38929         panel.setRegion(null);
38930         if(this.activePanel == panel){
38931             this.activePanel = null;
38932         }
38933         if(this.config.autoDestroy !== false && preservePanel !== true){
38934             try{panel.destroy();}catch(e){}
38935         }
38936         this.fireEvent("panelremoved", this, panel);
38937         return panel;
38938     },
38939
38940     /**
38941      * Returns the TabPanel component used by this region
38942      * @return {Roo.TabPanel}
38943      */
38944     getTabs : function(){
38945         return this.tabs;
38946     },
38947
38948     createTool : function(parentEl, className){
38949         var btn = Roo.DomHelper.append(parentEl, {
38950             tag: "div",
38951             cls: "x-layout-tools-button",
38952             children: [ {
38953                 tag: "div",
38954                 cls: "roo-layout-tools-button-inner " + className,
38955                 html: "&#160;"
38956             }]
38957         }, true);
38958         btn.addClassOnOver("roo-layout-tools-button-over");
38959         return btn;
38960     }
38961 });/*
38962  * Based on:
38963  * Ext JS Library 1.1.1
38964  * Copyright(c) 2006-2007, Ext JS, LLC.
38965  *
38966  * Originally Released Under LGPL - original licence link has changed is not relivant.
38967  *
38968  * Fork - LGPL
38969  * <script type="text/javascript">
38970  */
38971  
38972
38973
38974 /**
38975  * @class Roo.SplitLayoutRegion
38976  * @extends Roo.LayoutRegion
38977  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38978  */
38979 Roo.bootstrap.layout.Split = function(config){
38980     this.cursor = config.cursor;
38981     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38982 };
38983
38984 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38985 {
38986     splitTip : "Drag to resize.",
38987     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38988     useSplitTips : false,
38989
38990     applyConfig : function(config){
38991         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38992     },
38993     
38994     onRender : function(ctr,pos) {
38995         
38996         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38997         if(!this.config.split){
38998             return;
38999         }
39000         if(!this.split){
39001             
39002             var splitEl = Roo.DomHelper.append(ctr.dom,  {
39003                             tag: "div",
39004                             id: this.el.id + "-split",
39005                             cls: "roo-layout-split roo-layout-split-"+this.position,
39006                             html: "&#160;"
39007             });
39008             /** The SplitBar for this region 
39009             * @type Roo.SplitBar */
39010             // does not exist yet...
39011             Roo.log([this.position, this.orientation]);
39012             
39013             this.split = new Roo.bootstrap.SplitBar({
39014                 dragElement : splitEl,
39015                 resizingElement: this.el,
39016                 orientation : this.orientation
39017             });
39018             
39019             this.split.on("moved", this.onSplitMove, this);
39020             this.split.useShim = this.config.useShim === true;
39021             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39022             if(this.useSplitTips){
39023                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39024             }
39025             //if(config.collapsible){
39026             //    this.split.el.on("dblclick", this.collapse,  this);
39027             //}
39028         }
39029         if(typeof this.config.minSize != "undefined"){
39030             this.split.minSize = this.config.minSize;
39031         }
39032         if(typeof this.config.maxSize != "undefined"){
39033             this.split.maxSize = this.config.maxSize;
39034         }
39035         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39036             this.hideSplitter();
39037         }
39038         
39039     },
39040
39041     getHMaxSize : function(){
39042          var cmax = this.config.maxSize || 10000;
39043          var center = this.mgr.getRegion("center");
39044          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39045     },
39046
39047     getVMaxSize : function(){
39048          var cmax = this.config.maxSize || 10000;
39049          var center = this.mgr.getRegion("center");
39050          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39051     },
39052
39053     onSplitMove : function(split, newSize){
39054         this.fireEvent("resized", this, newSize);
39055     },
39056     
39057     /** 
39058      * Returns the {@link Roo.SplitBar} for this region.
39059      * @return {Roo.SplitBar}
39060      */
39061     getSplitBar : function(){
39062         return this.split;
39063     },
39064     
39065     hide : function(){
39066         this.hideSplitter();
39067         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39068     },
39069
39070     hideSplitter : function(){
39071         if(this.split){
39072             this.split.el.setLocation(-2000,-2000);
39073             this.split.el.hide();
39074         }
39075     },
39076
39077     show : function(){
39078         if(this.split){
39079             this.split.el.show();
39080         }
39081         Roo.bootstrap.layout.Split.superclass.show.call(this);
39082     },
39083     
39084     beforeSlide: function(){
39085         if(Roo.isGecko){// firefox overflow auto bug workaround
39086             this.bodyEl.clip();
39087             if(this.tabs) {
39088                 this.tabs.bodyEl.clip();
39089             }
39090             if(this.activePanel){
39091                 this.activePanel.getEl().clip();
39092                 
39093                 if(this.activePanel.beforeSlide){
39094                     this.activePanel.beforeSlide();
39095                 }
39096             }
39097         }
39098     },
39099     
39100     afterSlide : function(){
39101         if(Roo.isGecko){// firefox overflow auto bug workaround
39102             this.bodyEl.unclip();
39103             if(this.tabs) {
39104                 this.tabs.bodyEl.unclip();
39105             }
39106             if(this.activePanel){
39107                 this.activePanel.getEl().unclip();
39108                 if(this.activePanel.afterSlide){
39109                     this.activePanel.afterSlide();
39110                 }
39111             }
39112         }
39113     },
39114
39115     initAutoHide : function(){
39116         if(this.autoHide !== false){
39117             if(!this.autoHideHd){
39118                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39119                 this.autoHideHd = {
39120                     "mouseout": function(e){
39121                         if(!e.within(this.el, true)){
39122                             st.delay(500);
39123                         }
39124                     },
39125                     "mouseover" : function(e){
39126                         st.cancel();
39127                     },
39128                     scope : this
39129                 };
39130             }
39131             this.el.on(this.autoHideHd);
39132         }
39133     },
39134
39135     clearAutoHide : function(){
39136         if(this.autoHide !== false){
39137             this.el.un("mouseout", this.autoHideHd.mouseout);
39138             this.el.un("mouseover", this.autoHideHd.mouseover);
39139         }
39140     },
39141
39142     clearMonitor : function(){
39143         Roo.get(document).un("click", this.slideInIf, this);
39144     },
39145
39146     // these names are backwards but not changed for compat
39147     slideOut : function(){
39148         if(this.isSlid || this.el.hasActiveFx()){
39149             return;
39150         }
39151         this.isSlid = true;
39152         if(this.collapseBtn){
39153             this.collapseBtn.hide();
39154         }
39155         this.closeBtnState = this.closeBtn.getStyle('display');
39156         this.closeBtn.hide();
39157         if(this.stickBtn){
39158             this.stickBtn.show();
39159         }
39160         this.el.show();
39161         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39162         this.beforeSlide();
39163         this.el.setStyle("z-index", 10001);
39164         this.el.slideIn(this.getSlideAnchor(), {
39165             callback: function(){
39166                 this.afterSlide();
39167                 this.initAutoHide();
39168                 Roo.get(document).on("click", this.slideInIf, this);
39169                 this.fireEvent("slideshow", this);
39170             },
39171             scope: this,
39172             block: true
39173         });
39174     },
39175
39176     afterSlideIn : function(){
39177         this.clearAutoHide();
39178         this.isSlid = false;
39179         this.clearMonitor();
39180         this.el.setStyle("z-index", "");
39181         if(this.collapseBtn){
39182             this.collapseBtn.show();
39183         }
39184         this.closeBtn.setStyle('display', this.closeBtnState);
39185         if(this.stickBtn){
39186             this.stickBtn.hide();
39187         }
39188         this.fireEvent("slidehide", this);
39189     },
39190
39191     slideIn : function(cb){
39192         if(!this.isSlid || this.el.hasActiveFx()){
39193             Roo.callback(cb);
39194             return;
39195         }
39196         this.isSlid = false;
39197         this.beforeSlide();
39198         this.el.slideOut(this.getSlideAnchor(), {
39199             callback: function(){
39200                 this.el.setLeftTop(-10000, -10000);
39201                 this.afterSlide();
39202                 this.afterSlideIn();
39203                 Roo.callback(cb);
39204             },
39205             scope: this,
39206             block: true
39207         });
39208     },
39209     
39210     slideInIf : function(e){
39211         if(!e.within(this.el)){
39212             this.slideIn();
39213         }
39214     },
39215
39216     animateCollapse : function(){
39217         this.beforeSlide();
39218         this.el.setStyle("z-index", 20000);
39219         var anchor = this.getSlideAnchor();
39220         this.el.slideOut(anchor, {
39221             callback : function(){
39222                 this.el.setStyle("z-index", "");
39223                 this.collapsedEl.slideIn(anchor, {duration:.3});
39224                 this.afterSlide();
39225                 this.el.setLocation(-10000,-10000);
39226                 this.el.hide();
39227                 this.fireEvent("collapsed", this);
39228             },
39229             scope: this,
39230             block: true
39231         });
39232     },
39233
39234     animateExpand : function(){
39235         this.beforeSlide();
39236         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39237         this.el.setStyle("z-index", 20000);
39238         this.collapsedEl.hide({
39239             duration:.1
39240         });
39241         this.el.slideIn(this.getSlideAnchor(), {
39242             callback : function(){
39243                 this.el.setStyle("z-index", "");
39244                 this.afterSlide();
39245                 if(this.split){
39246                     this.split.el.show();
39247                 }
39248                 this.fireEvent("invalidated", this);
39249                 this.fireEvent("expanded", this);
39250             },
39251             scope: this,
39252             block: true
39253         });
39254     },
39255
39256     anchors : {
39257         "west" : "left",
39258         "east" : "right",
39259         "north" : "top",
39260         "south" : "bottom"
39261     },
39262
39263     sanchors : {
39264         "west" : "l",
39265         "east" : "r",
39266         "north" : "t",
39267         "south" : "b"
39268     },
39269
39270     canchors : {
39271         "west" : "tl-tr",
39272         "east" : "tr-tl",
39273         "north" : "tl-bl",
39274         "south" : "bl-tl"
39275     },
39276
39277     getAnchor : function(){
39278         return this.anchors[this.position];
39279     },
39280
39281     getCollapseAnchor : function(){
39282         return this.canchors[this.position];
39283     },
39284
39285     getSlideAnchor : function(){
39286         return this.sanchors[this.position];
39287     },
39288
39289     getAlignAdj : function(){
39290         var cm = this.cmargins;
39291         switch(this.position){
39292             case "west":
39293                 return [0, 0];
39294             break;
39295             case "east":
39296                 return [0, 0];
39297             break;
39298             case "north":
39299                 return [0, 0];
39300             break;
39301             case "south":
39302                 return [0, 0];
39303             break;
39304         }
39305     },
39306
39307     getExpandAdj : function(){
39308         var c = this.collapsedEl, cm = this.cmargins;
39309         switch(this.position){
39310             case "west":
39311                 return [-(cm.right+c.getWidth()+cm.left), 0];
39312             break;
39313             case "east":
39314                 return [cm.right+c.getWidth()+cm.left, 0];
39315             break;
39316             case "north":
39317                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39318             break;
39319             case "south":
39320                 return [0, cm.top+cm.bottom+c.getHeight()];
39321             break;
39322         }
39323     }
39324 });/*
39325  * Based on:
39326  * Ext JS Library 1.1.1
39327  * Copyright(c) 2006-2007, Ext JS, LLC.
39328  *
39329  * Originally Released Under LGPL - original licence link has changed is not relivant.
39330  *
39331  * Fork - LGPL
39332  * <script type="text/javascript">
39333  */
39334 /*
39335  * These classes are private internal classes
39336  */
39337 Roo.bootstrap.layout.Center = function(config){
39338     config.region = "center";
39339     Roo.bootstrap.layout.Region.call(this, config);
39340     this.visible = true;
39341     this.minWidth = config.minWidth || 20;
39342     this.minHeight = config.minHeight || 20;
39343 };
39344
39345 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39346     hide : function(){
39347         // center panel can't be hidden
39348     },
39349     
39350     show : function(){
39351         // center panel can't be hidden
39352     },
39353     
39354     getMinWidth: function(){
39355         return this.minWidth;
39356     },
39357     
39358     getMinHeight: function(){
39359         return this.minHeight;
39360     }
39361 });
39362
39363
39364
39365
39366  
39367
39368
39369
39370
39371
39372
39373 Roo.bootstrap.layout.North = function(config)
39374 {
39375     config.region = 'north';
39376     config.cursor = 'n-resize';
39377     
39378     Roo.bootstrap.layout.Split.call(this, config);
39379     
39380     
39381     if(this.split){
39382         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39383         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39384         this.split.el.addClass("roo-layout-split-v");
39385     }
39386     //var size = config.initialSize || config.height;
39387     //if(this.el && typeof size != "undefined"){
39388     //    this.el.setHeight(size);
39389     //}
39390 };
39391 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39392 {
39393     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39394      
39395      
39396     onRender : function(ctr, pos)
39397     {
39398         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39399         var size = this.config.initialSize || this.config.height;
39400         if(this.el && typeof size != "undefined"){
39401             this.el.setHeight(size);
39402         }
39403     
39404     },
39405     
39406     getBox : function(){
39407         if(this.collapsed){
39408             return this.collapsedEl.getBox();
39409         }
39410         var box = this.el.getBox();
39411         if(this.split){
39412             box.height += this.split.el.getHeight();
39413         }
39414         return box;
39415     },
39416     
39417     updateBox : function(box){
39418         if(this.split && !this.collapsed){
39419             box.height -= this.split.el.getHeight();
39420             this.split.el.setLeft(box.x);
39421             this.split.el.setTop(box.y+box.height);
39422             this.split.el.setWidth(box.width);
39423         }
39424         if(this.collapsed){
39425             this.updateBody(box.width, null);
39426         }
39427         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39428     }
39429 });
39430
39431
39432
39433
39434
39435 Roo.bootstrap.layout.South = function(config){
39436     config.region = 'south';
39437     config.cursor = 's-resize';
39438     Roo.bootstrap.layout.Split.call(this, config);
39439     if(this.split){
39440         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39441         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39442         this.split.el.addClass("roo-layout-split-v");
39443     }
39444     
39445 };
39446
39447 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39448     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39449     
39450     onRender : function(ctr, pos)
39451     {
39452         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39453         var size = this.config.initialSize || this.config.height;
39454         if(this.el && typeof size != "undefined"){
39455             this.el.setHeight(size);
39456         }
39457     
39458     },
39459     
39460     getBox : function(){
39461         if(this.collapsed){
39462             return this.collapsedEl.getBox();
39463         }
39464         var box = this.el.getBox();
39465         if(this.split){
39466             var sh = this.split.el.getHeight();
39467             box.height += sh;
39468             box.y -= sh;
39469         }
39470         return box;
39471     },
39472     
39473     updateBox : function(box){
39474         if(this.split && !this.collapsed){
39475             var sh = this.split.el.getHeight();
39476             box.height -= sh;
39477             box.y += sh;
39478             this.split.el.setLeft(box.x);
39479             this.split.el.setTop(box.y-sh);
39480             this.split.el.setWidth(box.width);
39481         }
39482         if(this.collapsed){
39483             this.updateBody(box.width, null);
39484         }
39485         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39486     }
39487 });
39488
39489 Roo.bootstrap.layout.East = function(config){
39490     config.region = "east";
39491     config.cursor = "e-resize";
39492     Roo.bootstrap.layout.Split.call(this, config);
39493     if(this.split){
39494         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39495         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39496         this.split.el.addClass("roo-layout-split-h");
39497     }
39498     
39499 };
39500 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39501     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39502     
39503     onRender : function(ctr, pos)
39504     {
39505         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39506         var size = this.config.initialSize || this.config.width;
39507         if(this.el && typeof size != "undefined"){
39508             this.el.setWidth(size);
39509         }
39510     
39511     },
39512     
39513     getBox : function(){
39514         if(this.collapsed){
39515             return this.collapsedEl.getBox();
39516         }
39517         var box = this.el.getBox();
39518         if(this.split){
39519             var sw = this.split.el.getWidth();
39520             box.width += sw;
39521             box.x -= sw;
39522         }
39523         return box;
39524     },
39525
39526     updateBox : function(box){
39527         if(this.split && !this.collapsed){
39528             var sw = this.split.el.getWidth();
39529             box.width -= sw;
39530             this.split.el.setLeft(box.x);
39531             this.split.el.setTop(box.y);
39532             this.split.el.setHeight(box.height);
39533             box.x += sw;
39534         }
39535         if(this.collapsed){
39536             this.updateBody(null, box.height);
39537         }
39538         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39539     }
39540 });
39541
39542 Roo.bootstrap.layout.West = function(config){
39543     config.region = "west";
39544     config.cursor = "w-resize";
39545     
39546     Roo.bootstrap.layout.Split.call(this, config);
39547     if(this.split){
39548         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39549         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39550         this.split.el.addClass("roo-layout-split-h");
39551     }
39552     
39553 };
39554 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39555     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39556     
39557     onRender: function(ctr, pos)
39558     {
39559         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39560         var size = this.config.initialSize || this.config.width;
39561         if(typeof size != "undefined"){
39562             this.el.setWidth(size);
39563         }
39564     },
39565     
39566     getBox : function(){
39567         if(this.collapsed){
39568             return this.collapsedEl.getBox();
39569         }
39570         var box = this.el.getBox();
39571         if (box.width == 0) {
39572             box.width = this.config.width; // kludge?
39573         }
39574         if(this.split){
39575             box.width += this.split.el.getWidth();
39576         }
39577         return box;
39578     },
39579     
39580     updateBox : function(box){
39581         if(this.split && !this.collapsed){
39582             var sw = this.split.el.getWidth();
39583             box.width -= sw;
39584             this.split.el.setLeft(box.x+box.width);
39585             this.split.el.setTop(box.y);
39586             this.split.el.setHeight(box.height);
39587         }
39588         if(this.collapsed){
39589             this.updateBody(null, box.height);
39590         }
39591         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39592     }
39593 });Roo.namespace("Roo.bootstrap.panel");/*
39594  * Based on:
39595  * Ext JS Library 1.1.1
39596  * Copyright(c) 2006-2007, Ext JS, LLC.
39597  *
39598  * Originally Released Under LGPL - original licence link has changed is not relivant.
39599  *
39600  * Fork - LGPL
39601  * <script type="text/javascript">
39602  */
39603 /**
39604  * @class Roo.ContentPanel
39605  * @extends Roo.util.Observable
39606  * A basic ContentPanel element.
39607  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39608  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39609  * @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
39610  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39611  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39612  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39613  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39614  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39615  * @cfg {String} title          The title for this panel
39616  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39617  * @cfg {String} url            Calls {@link #setUrl} with this value
39618  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39619  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39620  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39621  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39622  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39623  * @cfg {Boolean} badges render the badges
39624  * @cfg {String} cls  extra classes to use  
39625  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39626
39627  * @constructor
39628  * Create a new ContentPanel.
39629  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39630  * @param {String/Object} config A string to set only the title or a config object
39631  * @param {String} content (optional) Set the HTML content for this panel
39632  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39633  */
39634 Roo.bootstrap.panel.Content = function( config){
39635     
39636     this.tpl = config.tpl || false;
39637     
39638     var el = config.el;
39639     var content = config.content;
39640
39641     if(config.autoCreate){ // xtype is available if this is called from factory
39642         el = Roo.id();
39643     }
39644     this.el = Roo.get(el);
39645     if(!this.el && config && config.autoCreate){
39646         if(typeof config.autoCreate == "object"){
39647             if(!config.autoCreate.id){
39648                 config.autoCreate.id = config.id||el;
39649             }
39650             this.el = Roo.DomHelper.append(document.body,
39651                         config.autoCreate, true);
39652         }else{
39653             var elcfg =  {
39654                 tag: "div",
39655                 cls: (config.cls || '') +
39656                     (config.background ? ' bg-' + config.background : '') +
39657                     " roo-layout-inactive-content",
39658                 id: config.id||el
39659             };
39660             if (config.iframe) {
39661                 elcfg.cn = [
39662                     {
39663                         tag : 'iframe',
39664                         style : 'border: 0px',
39665                         src : 'about:blank'
39666                     }
39667                 ];
39668             }
39669               
39670             if (config.html) {
39671                 elcfg.html = config.html;
39672                 
39673             }
39674                         
39675             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39676             if (config.iframe) {
39677                 this.iframeEl = this.el.select('iframe',true).first();
39678             }
39679             
39680         }
39681     } 
39682     this.closable = false;
39683     this.loaded = false;
39684     this.active = false;
39685    
39686       
39687     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39688         
39689         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39690         
39691         this.wrapEl = this.el; //this.el.wrap();
39692         var ti = [];
39693         if (config.toolbar.items) {
39694             ti = config.toolbar.items ;
39695             delete config.toolbar.items ;
39696         }
39697         
39698         var nitems = [];
39699         this.toolbar.render(this.wrapEl, 'before');
39700         for(var i =0;i < ti.length;i++) {
39701           //  Roo.log(['add child', items[i]]);
39702             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39703         }
39704         this.toolbar.items = nitems;
39705         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39706         delete config.toolbar;
39707         
39708     }
39709     /*
39710     // xtype created footer. - not sure if will work as we normally have to render first..
39711     if (this.footer && !this.footer.el && this.footer.xtype) {
39712         if (!this.wrapEl) {
39713             this.wrapEl = this.el.wrap();
39714         }
39715     
39716         this.footer.container = this.wrapEl.createChild();
39717          
39718         this.footer = Roo.factory(this.footer, Roo);
39719         
39720     }
39721     */
39722     
39723      if(typeof config == "string"){
39724         this.title = config;
39725     }else{
39726         Roo.apply(this, config);
39727     }
39728     
39729     if(this.resizeEl){
39730         this.resizeEl = Roo.get(this.resizeEl, true);
39731     }else{
39732         this.resizeEl = this.el;
39733     }
39734     // handle view.xtype
39735     
39736  
39737     
39738     
39739     this.addEvents({
39740         /**
39741          * @event activate
39742          * Fires when this panel is activated. 
39743          * @param {Roo.ContentPanel} this
39744          */
39745         "activate" : true,
39746         /**
39747          * @event deactivate
39748          * Fires when this panel is activated. 
39749          * @param {Roo.ContentPanel} this
39750          */
39751         "deactivate" : true,
39752
39753         /**
39754          * @event resize
39755          * Fires when this panel is resized if fitToFrame is true.
39756          * @param {Roo.ContentPanel} this
39757          * @param {Number} width The width after any component adjustments
39758          * @param {Number} height The height after any component adjustments
39759          */
39760         "resize" : true,
39761         
39762          /**
39763          * @event render
39764          * Fires when this tab is created
39765          * @param {Roo.ContentPanel} this
39766          */
39767         "render" : true
39768         
39769         
39770         
39771     });
39772     
39773
39774     
39775     
39776     if(this.autoScroll && !this.iframe){
39777         this.resizeEl.setStyle("overflow", "auto");
39778     } else {
39779         // fix randome scrolling
39780         //this.el.on('scroll', function() {
39781         //    Roo.log('fix random scolling');
39782         //    this.scrollTo('top',0); 
39783         //});
39784     }
39785     content = content || this.content;
39786     if(content){
39787         this.setContent(content);
39788     }
39789     if(config && config.url){
39790         this.setUrl(this.url, this.params, this.loadOnce);
39791     }
39792     
39793     
39794     
39795     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39796     
39797     if (this.view && typeof(this.view.xtype) != 'undefined') {
39798         this.view.el = this.el.appendChild(document.createElement("div"));
39799         this.view = Roo.factory(this.view); 
39800         this.view.render  &&  this.view.render(false, '');  
39801     }
39802     
39803     
39804     this.fireEvent('render', this);
39805 };
39806
39807 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39808     
39809     cls : '',
39810     background : '',
39811     
39812     tabTip : '',
39813     
39814     iframe : false,
39815     iframeEl : false,
39816     
39817     setRegion : function(region){
39818         this.region = region;
39819         this.setActiveClass(region && !this.background);
39820     },
39821     
39822     
39823     setActiveClass: function(state)
39824     {
39825         if(state){
39826            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39827            this.el.setStyle('position','relative');
39828         }else{
39829            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39830            this.el.setStyle('position', 'absolute');
39831         } 
39832     },
39833     
39834     /**
39835      * Returns the toolbar for this Panel if one was configured. 
39836      * @return {Roo.Toolbar} 
39837      */
39838     getToolbar : function(){
39839         return this.toolbar;
39840     },
39841     
39842     setActiveState : function(active)
39843     {
39844         this.active = active;
39845         this.setActiveClass(active);
39846         if(!active){
39847             if(this.fireEvent("deactivate", this) === false){
39848                 return false;
39849             }
39850             return true;
39851         }
39852         this.fireEvent("activate", this);
39853         return true;
39854     },
39855     /**
39856      * Updates this panel's element (not for iframe)
39857      * @param {String} content The new content
39858      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39859     */
39860     setContent : function(content, loadScripts){
39861         if (this.iframe) {
39862             return;
39863         }
39864         
39865         this.el.update(content, loadScripts);
39866     },
39867
39868     ignoreResize : function(w, h){
39869         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39870             return true;
39871         }else{
39872             this.lastSize = {width: w, height: h};
39873             return false;
39874         }
39875     },
39876     /**
39877      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39878      * @return {Roo.UpdateManager} The UpdateManager
39879      */
39880     getUpdateManager : function(){
39881         if (this.iframe) {
39882             return false;
39883         }
39884         return this.el.getUpdateManager();
39885     },
39886      /**
39887      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39888      * Does not work with IFRAME contents
39889      * @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:
39890 <pre><code>
39891 panel.load({
39892     url: "your-url.php",
39893     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39894     callback: yourFunction,
39895     scope: yourObject, //(optional scope)
39896     discardUrl: false,
39897     nocache: false,
39898     text: "Loading...",
39899     timeout: 30,
39900     scripts: false
39901 });
39902 </code></pre>
39903      
39904      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39905      * 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.
39906      * @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}
39907      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39908      * @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.
39909      * @return {Roo.ContentPanel} this
39910      */
39911     load : function(){
39912         
39913         if (this.iframe) {
39914             return this;
39915         }
39916         
39917         var um = this.el.getUpdateManager();
39918         um.update.apply(um, arguments);
39919         return this;
39920     },
39921
39922
39923     /**
39924      * 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.
39925      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39926      * @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)
39927      * @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)
39928      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39929      */
39930     setUrl : function(url, params, loadOnce){
39931         if (this.iframe) {
39932             this.iframeEl.dom.src = url;
39933             return false;
39934         }
39935         
39936         if(this.refreshDelegate){
39937             this.removeListener("activate", this.refreshDelegate);
39938         }
39939         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39940         this.on("activate", this.refreshDelegate);
39941         return this.el.getUpdateManager();
39942     },
39943     
39944     _handleRefresh : function(url, params, loadOnce){
39945         if(!loadOnce || !this.loaded){
39946             var updater = this.el.getUpdateManager();
39947             updater.update(url, params, this._setLoaded.createDelegate(this));
39948         }
39949     },
39950     
39951     _setLoaded : function(){
39952         this.loaded = true;
39953     }, 
39954     
39955     /**
39956      * Returns this panel's id
39957      * @return {String} 
39958      */
39959     getId : function(){
39960         return this.el.id;
39961     },
39962     
39963     /** 
39964      * Returns this panel's element - used by regiosn to add.
39965      * @return {Roo.Element} 
39966      */
39967     getEl : function(){
39968         return this.wrapEl || this.el;
39969     },
39970     
39971    
39972     
39973     adjustForComponents : function(width, height)
39974     {
39975         //Roo.log('adjustForComponents ');
39976         if(this.resizeEl != this.el){
39977             width -= this.el.getFrameWidth('lr');
39978             height -= this.el.getFrameWidth('tb');
39979         }
39980         if(this.toolbar){
39981             var te = this.toolbar.getEl();
39982             te.setWidth(width);
39983             height -= te.getHeight();
39984         }
39985         if(this.footer){
39986             var te = this.footer.getEl();
39987             te.setWidth(width);
39988             height -= te.getHeight();
39989         }
39990         
39991         
39992         if(this.adjustments){
39993             width += this.adjustments[0];
39994             height += this.adjustments[1];
39995         }
39996         return {"width": width, "height": height};
39997     },
39998     
39999     setSize : function(width, height){
40000         if(this.fitToFrame && !this.ignoreResize(width, height)){
40001             if(this.fitContainer && this.resizeEl != this.el){
40002                 this.el.setSize(width, height);
40003             }
40004             var size = this.adjustForComponents(width, height);
40005             if (this.iframe) {
40006                 this.iframeEl.setSize(width,height);
40007             }
40008             
40009             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
40010             this.fireEvent('resize', this, size.width, size.height);
40011             
40012             
40013         }
40014     },
40015     
40016     /**
40017      * Returns this panel's title
40018      * @return {String} 
40019      */
40020     getTitle : function(){
40021         
40022         if (typeof(this.title) != 'object') {
40023             return this.title;
40024         }
40025         
40026         var t = '';
40027         for (var k in this.title) {
40028             if (!this.title.hasOwnProperty(k)) {
40029                 continue;
40030             }
40031             
40032             if (k.indexOf('-') >= 0) {
40033                 var s = k.split('-');
40034                 for (var i = 0; i<s.length; i++) {
40035                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40036                 }
40037             } else {
40038                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40039             }
40040         }
40041         return t;
40042     },
40043     
40044     /**
40045      * Set this panel's title
40046      * @param {String} title
40047      */
40048     setTitle : function(title){
40049         this.title = title;
40050         if(this.region){
40051             this.region.updatePanelTitle(this, title);
40052         }
40053     },
40054     
40055     /**
40056      * Returns true is this panel was configured to be closable
40057      * @return {Boolean} 
40058      */
40059     isClosable : function(){
40060         return this.closable;
40061     },
40062     
40063     beforeSlide : function(){
40064         this.el.clip();
40065         this.resizeEl.clip();
40066     },
40067     
40068     afterSlide : function(){
40069         this.el.unclip();
40070         this.resizeEl.unclip();
40071     },
40072     
40073     /**
40074      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40075      *   Will fail silently if the {@link #setUrl} method has not been called.
40076      *   This does not activate the panel, just updates its content.
40077      */
40078     refresh : function(){
40079         if(this.refreshDelegate){
40080            this.loaded = false;
40081            this.refreshDelegate();
40082         }
40083     },
40084     
40085     /**
40086      * Destroys this panel
40087      */
40088     destroy : function(){
40089         this.el.removeAllListeners();
40090         var tempEl = document.createElement("span");
40091         tempEl.appendChild(this.el.dom);
40092         tempEl.innerHTML = "";
40093         this.el.remove();
40094         this.el = null;
40095     },
40096     
40097     /**
40098      * form - if the content panel contains a form - this is a reference to it.
40099      * @type {Roo.form.Form}
40100      */
40101     form : false,
40102     /**
40103      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40104      *    This contains a reference to it.
40105      * @type {Roo.View}
40106      */
40107     view : false,
40108     
40109       /**
40110      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40111      * <pre><code>
40112
40113 layout.addxtype({
40114        xtype : 'Form',
40115        items: [ .... ]
40116    }
40117 );
40118
40119 </code></pre>
40120      * @param {Object} cfg Xtype definition of item to add.
40121      */
40122     
40123     
40124     getChildContainer: function () {
40125         return this.getEl();
40126     }
40127     
40128     
40129     /*
40130         var  ret = new Roo.factory(cfg);
40131         return ret;
40132         
40133         
40134         // add form..
40135         if (cfg.xtype.match(/^Form$/)) {
40136             
40137             var el;
40138             //if (this.footer) {
40139             //    el = this.footer.container.insertSibling(false, 'before');
40140             //} else {
40141                 el = this.el.createChild();
40142             //}
40143
40144             this.form = new  Roo.form.Form(cfg);
40145             
40146             
40147             if ( this.form.allItems.length) {
40148                 this.form.render(el.dom);
40149             }
40150             return this.form;
40151         }
40152         // should only have one of theses..
40153         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40154             // views.. should not be just added - used named prop 'view''
40155             
40156             cfg.el = this.el.appendChild(document.createElement("div"));
40157             // factory?
40158             
40159             var ret = new Roo.factory(cfg);
40160              
40161              ret.render && ret.render(false, ''); // render blank..
40162             this.view = ret;
40163             return ret;
40164         }
40165         return false;
40166     }
40167     \*/
40168 });
40169  
40170 /**
40171  * @class Roo.bootstrap.panel.Grid
40172  * @extends Roo.bootstrap.panel.Content
40173  * @constructor
40174  * Create a new GridPanel.
40175  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40176  * @param {Object} config A the config object
40177   
40178  */
40179
40180
40181
40182 Roo.bootstrap.panel.Grid = function(config)
40183 {
40184     
40185       
40186     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40187         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40188
40189     config.el = this.wrapper;
40190     //this.el = this.wrapper;
40191     
40192       if (config.container) {
40193         // ctor'ed from a Border/panel.grid
40194         
40195         
40196         this.wrapper.setStyle("overflow", "hidden");
40197         this.wrapper.addClass('roo-grid-container');
40198
40199     }
40200     
40201     
40202     if(config.toolbar){
40203         var tool_el = this.wrapper.createChild();    
40204         this.toolbar = Roo.factory(config.toolbar);
40205         var ti = [];
40206         if (config.toolbar.items) {
40207             ti = config.toolbar.items ;
40208             delete config.toolbar.items ;
40209         }
40210         
40211         var nitems = [];
40212         this.toolbar.render(tool_el);
40213         for(var i =0;i < ti.length;i++) {
40214           //  Roo.log(['add child', items[i]]);
40215             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40216         }
40217         this.toolbar.items = nitems;
40218         
40219         delete config.toolbar;
40220     }
40221     
40222     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40223     config.grid.scrollBody = true;;
40224     config.grid.monitorWindowResize = false; // turn off autosizing
40225     config.grid.autoHeight = false;
40226     config.grid.autoWidth = false;
40227     
40228     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40229     
40230     if (config.background) {
40231         // render grid on panel activation (if panel background)
40232         this.on('activate', function(gp) {
40233             if (!gp.grid.rendered) {
40234                 gp.grid.render(this.wrapper);
40235                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40236             }
40237         });
40238             
40239     } else {
40240         this.grid.render(this.wrapper);
40241         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40242
40243     }
40244     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40245     // ??? needed ??? config.el = this.wrapper;
40246     
40247     
40248     
40249   
40250     // xtype created footer. - not sure if will work as we normally have to render first..
40251     if (this.footer && !this.footer.el && this.footer.xtype) {
40252         
40253         var ctr = this.grid.getView().getFooterPanel(true);
40254         this.footer.dataSource = this.grid.dataSource;
40255         this.footer = Roo.factory(this.footer, Roo);
40256         this.footer.render(ctr);
40257         
40258     }
40259     
40260     
40261     
40262     
40263      
40264 };
40265
40266 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40267     getId : function(){
40268         return this.grid.id;
40269     },
40270     
40271     /**
40272      * Returns the grid for this panel
40273      * @return {Roo.bootstrap.Table} 
40274      */
40275     getGrid : function(){
40276         return this.grid;    
40277     },
40278     
40279     setSize : function(width, height){
40280         if(!this.ignoreResize(width, height)){
40281             var grid = this.grid;
40282             var size = this.adjustForComponents(width, height);
40283             // tfoot is not a footer?
40284           
40285             
40286             var gridel = grid.getGridEl();
40287             gridel.setSize(size.width, size.height);
40288             
40289             var tbd = grid.getGridEl().select('tbody', true).first();
40290             var thd = grid.getGridEl().select('thead',true).first();
40291             var tbf= grid.getGridEl().select('tfoot', true).first();
40292
40293             if (tbf) {
40294                 size.height -= tbf.getHeight();
40295             }
40296             if (thd) {
40297                 size.height -= thd.getHeight();
40298             }
40299             
40300             tbd.setSize(size.width, size.height );
40301             // this is for the account management tab -seems to work there.
40302             var thd = grid.getGridEl().select('thead',true).first();
40303             //if (tbd) {
40304             //    tbd.setSize(size.width, size.height - thd.getHeight());
40305             //}
40306              
40307             grid.autoSize();
40308         }
40309     },
40310      
40311     
40312     
40313     beforeSlide : function(){
40314         this.grid.getView().scroller.clip();
40315     },
40316     
40317     afterSlide : function(){
40318         this.grid.getView().scroller.unclip();
40319     },
40320     
40321     destroy : function(){
40322         this.grid.destroy();
40323         delete this.grid;
40324         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40325     }
40326 });
40327
40328 /**
40329  * @class Roo.bootstrap.panel.Nest
40330  * @extends Roo.bootstrap.panel.Content
40331  * @constructor
40332  * Create a new Panel, that can contain a layout.Border.
40333  * 
40334  * 
40335  * @param {Roo.BorderLayout} layout The layout for this panel
40336  * @param {String/Object} config A string to set only the title or a config object
40337  */
40338 Roo.bootstrap.panel.Nest = function(config)
40339 {
40340     // construct with only one argument..
40341     /* FIXME - implement nicer consturctors
40342     if (layout.layout) {
40343         config = layout;
40344         layout = config.layout;
40345         delete config.layout;
40346     }
40347     if (layout.xtype && !layout.getEl) {
40348         // then layout needs constructing..
40349         layout = Roo.factory(layout, Roo);
40350     }
40351     */
40352     
40353     config.el =  config.layout.getEl();
40354     
40355     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40356     
40357     config.layout.monitorWindowResize = false; // turn off autosizing
40358     this.layout = config.layout;
40359     this.layout.getEl().addClass("roo-layout-nested-layout");
40360     this.layout.parent = this;
40361     
40362     
40363     
40364     
40365 };
40366
40367 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40368
40369     setSize : function(width, height){
40370         if(!this.ignoreResize(width, height)){
40371             var size = this.adjustForComponents(width, height);
40372             var el = this.layout.getEl();
40373             if (size.height < 1) {
40374                 el.setWidth(size.width);   
40375             } else {
40376                 el.setSize(size.width, size.height);
40377             }
40378             var touch = el.dom.offsetWidth;
40379             this.layout.layout();
40380             // ie requires a double layout on the first pass
40381             if(Roo.isIE && !this.initialized){
40382                 this.initialized = true;
40383                 this.layout.layout();
40384             }
40385         }
40386     },
40387     
40388     // activate all subpanels if not currently active..
40389     
40390     setActiveState : function(active){
40391         this.active = active;
40392         this.setActiveClass(active);
40393         
40394         if(!active){
40395             this.fireEvent("deactivate", this);
40396             return;
40397         }
40398         
40399         this.fireEvent("activate", this);
40400         // not sure if this should happen before or after..
40401         if (!this.layout) {
40402             return; // should not happen..
40403         }
40404         var reg = false;
40405         for (var r in this.layout.regions) {
40406             reg = this.layout.getRegion(r);
40407             if (reg.getActivePanel()) {
40408                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40409                 reg.setActivePanel(reg.getActivePanel());
40410                 continue;
40411             }
40412             if (!reg.panels.length) {
40413                 continue;
40414             }
40415             reg.showPanel(reg.getPanel(0));
40416         }
40417         
40418         
40419         
40420         
40421     },
40422     
40423     /**
40424      * Returns the nested BorderLayout for this panel
40425      * @return {Roo.BorderLayout} 
40426      */
40427     getLayout : function(){
40428         return this.layout;
40429     },
40430     
40431      /**
40432      * Adds a xtype elements to the layout of the nested panel
40433      * <pre><code>
40434
40435 panel.addxtype({
40436        xtype : 'ContentPanel',
40437        region: 'west',
40438        items: [ .... ]
40439    }
40440 );
40441
40442 panel.addxtype({
40443         xtype : 'NestedLayoutPanel',
40444         region: 'west',
40445         layout: {
40446            center: { },
40447            west: { }   
40448         },
40449         items : [ ... list of content panels or nested layout panels.. ]
40450    }
40451 );
40452 </code></pre>
40453      * @param {Object} cfg Xtype definition of item to add.
40454      */
40455     addxtype : function(cfg) {
40456         return this.layout.addxtype(cfg);
40457     
40458     }
40459 });/*
40460  * Based on:
40461  * Ext JS Library 1.1.1
40462  * Copyright(c) 2006-2007, Ext JS, LLC.
40463  *
40464  * Originally Released Under LGPL - original licence link has changed is not relivant.
40465  *
40466  * Fork - LGPL
40467  * <script type="text/javascript">
40468  */
40469 /**
40470  * @class Roo.TabPanel
40471  * @extends Roo.util.Observable
40472  * A lightweight tab container.
40473  * <br><br>
40474  * Usage:
40475  * <pre><code>
40476 // basic tabs 1, built from existing content
40477 var tabs = new Roo.TabPanel("tabs1");
40478 tabs.addTab("script", "View Script");
40479 tabs.addTab("markup", "View Markup");
40480 tabs.activate("script");
40481
40482 // more advanced tabs, built from javascript
40483 var jtabs = new Roo.TabPanel("jtabs");
40484 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40485
40486 // set up the UpdateManager
40487 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40488 var updater = tab2.getUpdateManager();
40489 updater.setDefaultUrl("ajax1.htm");
40490 tab2.on('activate', updater.refresh, updater, true);
40491
40492 // Use setUrl for Ajax loading
40493 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40494 tab3.setUrl("ajax2.htm", null, true);
40495
40496 // Disabled tab
40497 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40498 tab4.disable();
40499
40500 jtabs.activate("jtabs-1");
40501  * </code></pre>
40502  * @constructor
40503  * Create a new TabPanel.
40504  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40505  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40506  */
40507 Roo.bootstrap.panel.Tabs = function(config){
40508     /**
40509     * The container element for this TabPanel.
40510     * @type Roo.Element
40511     */
40512     this.el = Roo.get(config.el);
40513     delete config.el;
40514     if(config){
40515         if(typeof config == "boolean"){
40516             this.tabPosition = config ? "bottom" : "top";
40517         }else{
40518             Roo.apply(this, config);
40519         }
40520     }
40521     
40522     if(this.tabPosition == "bottom"){
40523         // if tabs are at the bottom = create the body first.
40524         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40525         this.el.addClass("roo-tabs-bottom");
40526     }
40527     // next create the tabs holders
40528     
40529     if (this.tabPosition == "west"){
40530         
40531         var reg = this.region; // fake it..
40532         while (reg) {
40533             if (!reg.mgr.parent) {
40534                 break;
40535             }
40536             reg = reg.mgr.parent.region;
40537         }
40538         Roo.log("got nest?");
40539         Roo.log(reg);
40540         if (reg.mgr.getRegion('west')) {
40541             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40542             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40543             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40544             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40545             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40546         
40547             
40548         }
40549         
40550         
40551     } else {
40552      
40553         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40554         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40555         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40556         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40557     }
40558     
40559     
40560     if(Roo.isIE){
40561         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40562     }
40563     
40564     // finally - if tabs are at the top, then create the body last..
40565     if(this.tabPosition != "bottom"){
40566         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40567          * @type Roo.Element
40568          */
40569         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40570         this.el.addClass("roo-tabs-top");
40571     }
40572     this.items = [];
40573
40574     this.bodyEl.setStyle("position", "relative");
40575
40576     this.active = null;
40577     this.activateDelegate = this.activate.createDelegate(this);
40578
40579     this.addEvents({
40580         /**
40581          * @event tabchange
40582          * Fires when the active tab changes
40583          * @param {Roo.TabPanel} this
40584          * @param {Roo.TabPanelItem} activePanel The new active tab
40585          */
40586         "tabchange": true,
40587         /**
40588          * @event beforetabchange
40589          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40590          * @param {Roo.TabPanel} this
40591          * @param {Object} e Set cancel to true on this object to cancel the tab change
40592          * @param {Roo.TabPanelItem} tab The tab being changed to
40593          */
40594         "beforetabchange" : true
40595     });
40596
40597     Roo.EventManager.onWindowResize(this.onResize, this);
40598     this.cpad = this.el.getPadding("lr");
40599     this.hiddenCount = 0;
40600
40601
40602     // toolbar on the tabbar support...
40603     if (this.toolbar) {
40604         alert("no toolbar support yet");
40605         this.toolbar  = false;
40606         /*
40607         var tcfg = this.toolbar;
40608         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40609         this.toolbar = new Roo.Toolbar(tcfg);
40610         if (Roo.isSafari) {
40611             var tbl = tcfg.container.child('table', true);
40612             tbl.setAttribute('width', '100%');
40613         }
40614         */
40615         
40616     }
40617    
40618
40619
40620     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40621 };
40622
40623 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40624     /*
40625      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40626      */
40627     tabPosition : "top",
40628     /*
40629      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40630      */
40631     currentTabWidth : 0,
40632     /*
40633      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40634      */
40635     minTabWidth : 40,
40636     /*
40637      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40638      */
40639     maxTabWidth : 250,
40640     /*
40641      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40642      */
40643     preferredTabWidth : 175,
40644     /*
40645      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40646      */
40647     resizeTabs : false,
40648     /*
40649      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40650      */
40651     monitorResize : true,
40652     /*
40653      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40654      */
40655     toolbar : false,  // set by caller..
40656     
40657     region : false, /// set by caller
40658     
40659     disableTooltips : true, // not used yet...
40660
40661     /**
40662      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40663      * @param {String} id The id of the div to use <b>or create</b>
40664      * @param {String} text The text for the tab
40665      * @param {String} content (optional) Content to put in the TabPanelItem body
40666      * @param {Boolean} closable (optional) True to create a close icon on the tab
40667      * @return {Roo.TabPanelItem} The created TabPanelItem
40668      */
40669     addTab : function(id, text, content, closable, tpl)
40670     {
40671         var item = new Roo.bootstrap.panel.TabItem({
40672             panel: this,
40673             id : id,
40674             text : text,
40675             closable : closable,
40676             tpl : tpl
40677         });
40678         this.addTabItem(item);
40679         if(content){
40680             item.setContent(content);
40681         }
40682         return item;
40683     },
40684
40685     /**
40686      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40687      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40688      * @return {Roo.TabPanelItem}
40689      */
40690     getTab : function(id){
40691         return this.items[id];
40692     },
40693
40694     /**
40695      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40696      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40697      */
40698     hideTab : function(id){
40699         var t = this.items[id];
40700         if(!t.isHidden()){
40701            t.setHidden(true);
40702            this.hiddenCount++;
40703            this.autoSizeTabs();
40704         }
40705     },
40706
40707     /**
40708      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40709      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40710      */
40711     unhideTab : function(id){
40712         var t = this.items[id];
40713         if(t.isHidden()){
40714            t.setHidden(false);
40715            this.hiddenCount--;
40716            this.autoSizeTabs();
40717         }
40718     },
40719
40720     /**
40721      * Adds an existing {@link Roo.TabPanelItem}.
40722      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40723      */
40724     addTabItem : function(item)
40725     {
40726         this.items[item.id] = item;
40727         this.items.push(item);
40728         this.autoSizeTabs();
40729       //  if(this.resizeTabs){
40730     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40731   //         this.autoSizeTabs();
40732 //        }else{
40733 //            item.autoSize();
40734        // }
40735     },
40736
40737     /**
40738      * Removes a {@link Roo.TabPanelItem}.
40739      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40740      */
40741     removeTab : function(id){
40742         var items = this.items;
40743         var tab = items[id];
40744         if(!tab) { return; }
40745         var index = items.indexOf(tab);
40746         if(this.active == tab && items.length > 1){
40747             var newTab = this.getNextAvailable(index);
40748             if(newTab) {
40749                 newTab.activate();
40750             }
40751         }
40752         this.stripEl.dom.removeChild(tab.pnode.dom);
40753         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40754             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40755         }
40756         items.splice(index, 1);
40757         delete this.items[tab.id];
40758         tab.fireEvent("close", tab);
40759         tab.purgeListeners();
40760         this.autoSizeTabs();
40761     },
40762
40763     getNextAvailable : function(start){
40764         var items = this.items;
40765         var index = start;
40766         // look for a next tab that will slide over to
40767         // replace the one being removed
40768         while(index < items.length){
40769             var item = items[++index];
40770             if(item && !item.isHidden()){
40771                 return item;
40772             }
40773         }
40774         // if one isn't found select the previous tab (on the left)
40775         index = start;
40776         while(index >= 0){
40777             var item = items[--index];
40778             if(item && !item.isHidden()){
40779                 return item;
40780             }
40781         }
40782         return null;
40783     },
40784
40785     /**
40786      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40787      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40788      */
40789     disableTab : function(id){
40790         var tab = this.items[id];
40791         if(tab && this.active != tab){
40792             tab.disable();
40793         }
40794     },
40795
40796     /**
40797      * Enables a {@link Roo.TabPanelItem} that is disabled.
40798      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40799      */
40800     enableTab : function(id){
40801         var tab = this.items[id];
40802         tab.enable();
40803     },
40804
40805     /**
40806      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40807      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40808      * @return {Roo.TabPanelItem} The TabPanelItem.
40809      */
40810     activate : function(id)
40811     {
40812         //Roo.log('activite:'  + id);
40813         
40814         var tab = this.items[id];
40815         if(!tab){
40816             return null;
40817         }
40818         if(tab == this.active || tab.disabled){
40819             return tab;
40820         }
40821         var e = {};
40822         this.fireEvent("beforetabchange", this, e, tab);
40823         if(e.cancel !== true && !tab.disabled){
40824             if(this.active){
40825                 this.active.hide();
40826             }
40827             this.active = this.items[id];
40828             this.active.show();
40829             this.fireEvent("tabchange", this, this.active);
40830         }
40831         return tab;
40832     },
40833
40834     /**
40835      * Gets the active {@link Roo.TabPanelItem}.
40836      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40837      */
40838     getActiveTab : function(){
40839         return this.active;
40840     },
40841
40842     /**
40843      * Updates the tab body element to fit the height of the container element
40844      * for overflow scrolling
40845      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40846      */
40847     syncHeight : function(targetHeight){
40848         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40849         var bm = this.bodyEl.getMargins();
40850         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40851         this.bodyEl.setHeight(newHeight);
40852         return newHeight;
40853     },
40854
40855     onResize : function(){
40856         if(this.monitorResize){
40857             this.autoSizeTabs();
40858         }
40859     },
40860
40861     /**
40862      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40863      */
40864     beginUpdate : function(){
40865         this.updating = true;
40866     },
40867
40868     /**
40869      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40870      */
40871     endUpdate : function(){
40872         this.updating = false;
40873         this.autoSizeTabs();
40874     },
40875
40876     /**
40877      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40878      */
40879     autoSizeTabs : function()
40880     {
40881         var count = this.items.length;
40882         var vcount = count - this.hiddenCount;
40883         
40884         if (vcount < 2) {
40885             this.stripEl.hide();
40886         } else {
40887             this.stripEl.show();
40888         }
40889         
40890         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40891             return;
40892         }
40893         
40894         
40895         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40896         var availWidth = Math.floor(w / vcount);
40897         var b = this.stripBody;
40898         if(b.getWidth() > w){
40899             var tabs = this.items;
40900             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40901             if(availWidth < this.minTabWidth){
40902                 /*if(!this.sleft){    // incomplete scrolling code
40903                     this.createScrollButtons();
40904                 }
40905                 this.showScroll();
40906                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40907             }
40908         }else{
40909             if(this.currentTabWidth < this.preferredTabWidth){
40910                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40911             }
40912         }
40913     },
40914
40915     /**
40916      * Returns the number of tabs in this TabPanel.
40917      * @return {Number}
40918      */
40919      getCount : function(){
40920          return this.items.length;
40921      },
40922
40923     /**
40924      * Resizes all the tabs to the passed width
40925      * @param {Number} The new width
40926      */
40927     setTabWidth : function(width){
40928         this.currentTabWidth = width;
40929         for(var i = 0, len = this.items.length; i < len; i++) {
40930                 if(!this.items[i].isHidden()) {
40931                 this.items[i].setWidth(width);
40932             }
40933         }
40934     },
40935
40936     /**
40937      * Destroys this TabPanel
40938      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40939      */
40940     destroy : function(removeEl){
40941         Roo.EventManager.removeResizeListener(this.onResize, this);
40942         for(var i = 0, len = this.items.length; i < len; i++){
40943             this.items[i].purgeListeners();
40944         }
40945         if(removeEl === true){
40946             this.el.update("");
40947             this.el.remove();
40948         }
40949     },
40950     
40951     createStrip : function(container)
40952     {
40953         var strip = document.createElement("nav");
40954         strip.className = Roo.bootstrap.version == 4 ?
40955             "navbar-light bg-light" : 
40956             "navbar navbar-default"; //"x-tabs-wrap";
40957         container.appendChild(strip);
40958         return strip;
40959     },
40960     
40961     createStripList : function(strip)
40962     {
40963         // div wrapper for retard IE
40964         // returns the "tr" element.
40965         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40966         //'<div class="x-tabs-strip-wrap">'+
40967           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40968           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40969         return strip.firstChild; //.firstChild.firstChild.firstChild;
40970     },
40971     createBody : function(container)
40972     {
40973         var body = document.createElement("div");
40974         Roo.id(body, "tab-body");
40975         //Roo.fly(body).addClass("x-tabs-body");
40976         Roo.fly(body).addClass("tab-content");
40977         container.appendChild(body);
40978         return body;
40979     },
40980     createItemBody :function(bodyEl, id){
40981         var body = Roo.getDom(id);
40982         if(!body){
40983             body = document.createElement("div");
40984             body.id = id;
40985         }
40986         //Roo.fly(body).addClass("x-tabs-item-body");
40987         Roo.fly(body).addClass("tab-pane");
40988          bodyEl.insertBefore(body, bodyEl.firstChild);
40989         return body;
40990     },
40991     /** @private */
40992     createStripElements :  function(stripEl, text, closable, tpl)
40993     {
40994         var td = document.createElement("li"); // was td..
40995         td.className = 'nav-item';
40996         
40997         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40998         
40999         
41000         stripEl.appendChild(td);
41001         /*if(closable){
41002             td.className = "x-tabs-closable";
41003             if(!this.closeTpl){
41004                 this.closeTpl = new Roo.Template(
41005                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41006                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
41007                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
41008                 );
41009             }
41010             var el = this.closeTpl.overwrite(td, {"text": text});
41011             var close = el.getElementsByTagName("div")[0];
41012             var inner = el.getElementsByTagName("em")[0];
41013             return {"el": el, "close": close, "inner": inner};
41014         } else {
41015         */
41016         // not sure what this is..
41017 //            if(!this.tabTpl){
41018                 //this.tabTpl = new Roo.Template(
41019                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
41020                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41021                 //);
41022 //                this.tabTpl = new Roo.Template(
41023 //                   '<a href="#">' +
41024 //                   '<span unselectable="on"' +
41025 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41026 //                            ' >{text}</span></a>'
41027 //                );
41028 //                
41029 //            }
41030
41031
41032             var template = tpl || this.tabTpl || false;
41033             
41034             if(!template){
41035                 template =  new Roo.Template(
41036                         Roo.bootstrap.version == 4 ? 
41037                             (
41038                                 '<a class="nav-link" href="#" unselectable="on"' +
41039                                      (this.disableTooltips ? '' : ' title="{text}"') +
41040                                      ' >{text}</a>'
41041                             ) : (
41042                                 '<a class="nav-link" href="#">' +
41043                                 '<span unselectable="on"' +
41044                                          (this.disableTooltips ? '' : ' title="{text}"') +
41045                                     ' >{text}</span></a>'
41046                             )
41047                 );
41048             }
41049             
41050             switch (typeof(template)) {
41051                 case 'object' :
41052                     break;
41053                 case 'string' :
41054                     template = new Roo.Template(template);
41055                     break;
41056                 default :
41057                     break;
41058             }
41059             
41060             var el = template.overwrite(td, {"text": text});
41061             
41062             var inner = el.getElementsByTagName("span")[0];
41063             
41064             return {"el": el, "inner": inner};
41065             
41066     }
41067         
41068     
41069 });
41070
41071 /**
41072  * @class Roo.TabPanelItem
41073  * @extends Roo.util.Observable
41074  * Represents an individual item (tab plus body) in a TabPanel.
41075  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41076  * @param {String} id The id of this TabPanelItem
41077  * @param {String} text The text for the tab of this TabPanelItem
41078  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41079  */
41080 Roo.bootstrap.panel.TabItem = function(config){
41081     /**
41082      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41083      * @type Roo.TabPanel
41084      */
41085     this.tabPanel = config.panel;
41086     /**
41087      * The id for this TabPanelItem
41088      * @type String
41089      */
41090     this.id = config.id;
41091     /** @private */
41092     this.disabled = false;
41093     /** @private */
41094     this.text = config.text;
41095     /** @private */
41096     this.loaded = false;
41097     this.closable = config.closable;
41098
41099     /**
41100      * The body element for this TabPanelItem.
41101      * @type Roo.Element
41102      */
41103     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41104     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41105     this.bodyEl.setStyle("display", "block");
41106     this.bodyEl.setStyle("zoom", "1");
41107     //this.hideAction();
41108
41109     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41110     /** @private */
41111     this.el = Roo.get(els.el);
41112     this.inner = Roo.get(els.inner, true);
41113      this.textEl = Roo.bootstrap.version == 4 ?
41114         this.el : Roo.get(this.el.dom.firstChild, true);
41115
41116     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41117     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41118
41119     
41120 //    this.el.on("mousedown", this.onTabMouseDown, this);
41121     this.el.on("click", this.onTabClick, this);
41122     /** @private */
41123     if(config.closable){
41124         var c = Roo.get(els.close, true);
41125         c.dom.title = this.closeText;
41126         c.addClassOnOver("close-over");
41127         c.on("click", this.closeClick, this);
41128      }
41129
41130     this.addEvents({
41131          /**
41132          * @event activate
41133          * Fires when this tab becomes the active tab.
41134          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41135          * @param {Roo.TabPanelItem} this
41136          */
41137         "activate": true,
41138         /**
41139          * @event beforeclose
41140          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41141          * @param {Roo.TabPanelItem} this
41142          * @param {Object} e Set cancel to true on this object to cancel the close.
41143          */
41144         "beforeclose": true,
41145         /**
41146          * @event close
41147          * Fires when this tab is closed.
41148          * @param {Roo.TabPanelItem} this
41149          */
41150          "close": true,
41151         /**
41152          * @event deactivate
41153          * Fires when this tab is no longer the active tab.
41154          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41155          * @param {Roo.TabPanelItem} this
41156          */
41157          "deactivate" : true
41158     });
41159     this.hidden = false;
41160
41161     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41162 };
41163
41164 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41165            {
41166     purgeListeners : function(){
41167        Roo.util.Observable.prototype.purgeListeners.call(this);
41168        this.el.removeAllListeners();
41169     },
41170     /**
41171      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41172      */
41173     show : function(){
41174         this.status_node.addClass("active");
41175         this.showAction();
41176         if(Roo.isOpera){
41177             this.tabPanel.stripWrap.repaint();
41178         }
41179         this.fireEvent("activate", this.tabPanel, this);
41180     },
41181
41182     /**
41183      * Returns true if this tab is the active tab.
41184      * @return {Boolean}
41185      */
41186     isActive : function(){
41187         return this.tabPanel.getActiveTab() == this;
41188     },
41189
41190     /**
41191      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41192      */
41193     hide : function(){
41194         this.status_node.removeClass("active");
41195         this.hideAction();
41196         this.fireEvent("deactivate", this.tabPanel, this);
41197     },
41198
41199     hideAction : function(){
41200         this.bodyEl.hide();
41201         this.bodyEl.setStyle("position", "absolute");
41202         this.bodyEl.setLeft("-20000px");
41203         this.bodyEl.setTop("-20000px");
41204     },
41205
41206     showAction : function(){
41207         this.bodyEl.setStyle("position", "relative");
41208         this.bodyEl.setTop("");
41209         this.bodyEl.setLeft("");
41210         this.bodyEl.show();
41211     },
41212
41213     /**
41214      * Set the tooltip for the tab.
41215      * @param {String} tooltip The tab's tooltip
41216      */
41217     setTooltip : function(text){
41218         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41219             this.textEl.dom.qtip = text;
41220             this.textEl.dom.removeAttribute('title');
41221         }else{
41222             this.textEl.dom.title = text;
41223         }
41224     },
41225
41226     onTabClick : function(e){
41227         e.preventDefault();
41228         this.tabPanel.activate(this.id);
41229     },
41230
41231     onTabMouseDown : function(e){
41232         e.preventDefault();
41233         this.tabPanel.activate(this.id);
41234     },
41235 /*
41236     getWidth : function(){
41237         return this.inner.getWidth();
41238     },
41239
41240     setWidth : function(width){
41241         var iwidth = width - this.linode.getPadding("lr");
41242         this.inner.setWidth(iwidth);
41243         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41244         this.linode.setWidth(width);
41245     },
41246 */
41247     /**
41248      * Show or hide the tab
41249      * @param {Boolean} hidden True to hide or false to show.
41250      */
41251     setHidden : function(hidden){
41252         this.hidden = hidden;
41253         this.linode.setStyle("display", hidden ? "none" : "");
41254     },
41255
41256     /**
41257      * Returns true if this tab is "hidden"
41258      * @return {Boolean}
41259      */
41260     isHidden : function(){
41261         return this.hidden;
41262     },
41263
41264     /**
41265      * Returns the text for this tab
41266      * @return {String}
41267      */
41268     getText : function(){
41269         return this.text;
41270     },
41271     /*
41272     autoSize : function(){
41273         //this.el.beginMeasure();
41274         this.textEl.setWidth(1);
41275         /*
41276          *  #2804 [new] Tabs in Roojs
41277          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41278          */
41279         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41280         //this.el.endMeasure();
41281     //},
41282
41283     /**
41284      * Sets the text for the tab (Note: this also sets the tooltip text)
41285      * @param {String} text The tab's text and tooltip
41286      */
41287     setText : function(text){
41288         this.text = text;
41289         this.textEl.update(text);
41290         this.setTooltip(text);
41291         //if(!this.tabPanel.resizeTabs){
41292         //    this.autoSize();
41293         //}
41294     },
41295     /**
41296      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41297      */
41298     activate : function(){
41299         this.tabPanel.activate(this.id);
41300     },
41301
41302     /**
41303      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41304      */
41305     disable : function(){
41306         if(this.tabPanel.active != this){
41307             this.disabled = true;
41308             this.status_node.addClass("disabled");
41309         }
41310     },
41311
41312     /**
41313      * Enables this TabPanelItem if it was previously disabled.
41314      */
41315     enable : function(){
41316         this.disabled = false;
41317         this.status_node.removeClass("disabled");
41318     },
41319
41320     /**
41321      * Sets the content for this TabPanelItem.
41322      * @param {String} content The content
41323      * @param {Boolean} loadScripts true to look for and load scripts
41324      */
41325     setContent : function(content, loadScripts){
41326         this.bodyEl.update(content, loadScripts);
41327     },
41328
41329     /**
41330      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41331      * @return {Roo.UpdateManager} The UpdateManager
41332      */
41333     getUpdateManager : function(){
41334         return this.bodyEl.getUpdateManager();
41335     },
41336
41337     /**
41338      * Set a URL to be used to load the content for this TabPanelItem.
41339      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41340      * @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)
41341      * @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)
41342      * @return {Roo.UpdateManager} The UpdateManager
41343      */
41344     setUrl : function(url, params, loadOnce){
41345         if(this.refreshDelegate){
41346             this.un('activate', this.refreshDelegate);
41347         }
41348         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41349         this.on("activate", this.refreshDelegate);
41350         return this.bodyEl.getUpdateManager();
41351     },
41352
41353     /** @private */
41354     _handleRefresh : function(url, params, loadOnce){
41355         if(!loadOnce || !this.loaded){
41356             var updater = this.bodyEl.getUpdateManager();
41357             updater.update(url, params, this._setLoaded.createDelegate(this));
41358         }
41359     },
41360
41361     /**
41362      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41363      *   Will fail silently if the setUrl method has not been called.
41364      *   This does not activate the panel, just updates its content.
41365      */
41366     refresh : function(){
41367         if(this.refreshDelegate){
41368            this.loaded = false;
41369            this.refreshDelegate();
41370         }
41371     },
41372
41373     /** @private */
41374     _setLoaded : function(){
41375         this.loaded = true;
41376     },
41377
41378     /** @private */
41379     closeClick : function(e){
41380         var o = {};
41381         e.stopEvent();
41382         this.fireEvent("beforeclose", this, o);
41383         if(o.cancel !== true){
41384             this.tabPanel.removeTab(this.id);
41385         }
41386     },
41387     /**
41388      * The text displayed in the tooltip for the close icon.
41389      * @type String
41390      */
41391     closeText : "Close this tab"
41392 });
41393 /**
41394 *    This script refer to:
41395 *    Title: International Telephone Input
41396 *    Author: Jack O'Connor
41397 *    Code version:  v12.1.12
41398 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41399 **/
41400
41401 Roo.bootstrap.PhoneInputData = function() {
41402     var d = [
41403       [
41404         "Afghanistan (‫افغانستان‬‎)",
41405         "af",
41406         "93"
41407       ],
41408       [
41409         "Albania (Shqipëri)",
41410         "al",
41411         "355"
41412       ],
41413       [
41414         "Algeria (‫الجزائر‬‎)",
41415         "dz",
41416         "213"
41417       ],
41418       [
41419         "American Samoa",
41420         "as",
41421         "1684"
41422       ],
41423       [
41424         "Andorra",
41425         "ad",
41426         "376"
41427       ],
41428       [
41429         "Angola",
41430         "ao",
41431         "244"
41432       ],
41433       [
41434         "Anguilla",
41435         "ai",
41436         "1264"
41437       ],
41438       [
41439         "Antigua and Barbuda",
41440         "ag",
41441         "1268"
41442       ],
41443       [
41444         "Argentina",
41445         "ar",
41446         "54"
41447       ],
41448       [
41449         "Armenia (Հայաստան)",
41450         "am",
41451         "374"
41452       ],
41453       [
41454         "Aruba",
41455         "aw",
41456         "297"
41457       ],
41458       [
41459         "Australia",
41460         "au",
41461         "61",
41462         0
41463       ],
41464       [
41465         "Austria (Österreich)",
41466         "at",
41467         "43"
41468       ],
41469       [
41470         "Azerbaijan (Azərbaycan)",
41471         "az",
41472         "994"
41473       ],
41474       [
41475         "Bahamas",
41476         "bs",
41477         "1242"
41478       ],
41479       [
41480         "Bahrain (‫البحرين‬‎)",
41481         "bh",
41482         "973"
41483       ],
41484       [
41485         "Bangladesh (বাংলাদেশ)",
41486         "bd",
41487         "880"
41488       ],
41489       [
41490         "Barbados",
41491         "bb",
41492         "1246"
41493       ],
41494       [
41495         "Belarus (Беларусь)",
41496         "by",
41497         "375"
41498       ],
41499       [
41500         "Belgium (België)",
41501         "be",
41502         "32"
41503       ],
41504       [
41505         "Belize",
41506         "bz",
41507         "501"
41508       ],
41509       [
41510         "Benin (Bénin)",
41511         "bj",
41512         "229"
41513       ],
41514       [
41515         "Bermuda",
41516         "bm",
41517         "1441"
41518       ],
41519       [
41520         "Bhutan (འབྲུག)",
41521         "bt",
41522         "975"
41523       ],
41524       [
41525         "Bolivia",
41526         "bo",
41527         "591"
41528       ],
41529       [
41530         "Bosnia and Herzegovina (Босна и Херцеговина)",
41531         "ba",
41532         "387"
41533       ],
41534       [
41535         "Botswana",
41536         "bw",
41537         "267"
41538       ],
41539       [
41540         "Brazil (Brasil)",
41541         "br",
41542         "55"
41543       ],
41544       [
41545         "British Indian Ocean Territory",
41546         "io",
41547         "246"
41548       ],
41549       [
41550         "British Virgin Islands",
41551         "vg",
41552         "1284"
41553       ],
41554       [
41555         "Brunei",
41556         "bn",
41557         "673"
41558       ],
41559       [
41560         "Bulgaria (България)",
41561         "bg",
41562         "359"
41563       ],
41564       [
41565         "Burkina Faso",
41566         "bf",
41567         "226"
41568       ],
41569       [
41570         "Burundi (Uburundi)",
41571         "bi",
41572         "257"
41573       ],
41574       [
41575         "Cambodia (កម្ពុជា)",
41576         "kh",
41577         "855"
41578       ],
41579       [
41580         "Cameroon (Cameroun)",
41581         "cm",
41582         "237"
41583       ],
41584       [
41585         "Canada",
41586         "ca",
41587         "1",
41588         1,
41589         ["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"]
41590       ],
41591       [
41592         "Cape Verde (Kabu Verdi)",
41593         "cv",
41594         "238"
41595       ],
41596       [
41597         "Caribbean Netherlands",
41598         "bq",
41599         "599",
41600         1
41601       ],
41602       [
41603         "Cayman Islands",
41604         "ky",
41605         "1345"
41606       ],
41607       [
41608         "Central African Republic (République centrafricaine)",
41609         "cf",
41610         "236"
41611       ],
41612       [
41613         "Chad (Tchad)",
41614         "td",
41615         "235"
41616       ],
41617       [
41618         "Chile",
41619         "cl",
41620         "56"
41621       ],
41622       [
41623         "China (中国)",
41624         "cn",
41625         "86"
41626       ],
41627       [
41628         "Christmas Island",
41629         "cx",
41630         "61",
41631         2
41632       ],
41633       [
41634         "Cocos (Keeling) Islands",
41635         "cc",
41636         "61",
41637         1
41638       ],
41639       [
41640         "Colombia",
41641         "co",
41642         "57"
41643       ],
41644       [
41645         "Comoros (‫جزر القمر‬‎)",
41646         "km",
41647         "269"
41648       ],
41649       [
41650         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41651         "cd",
41652         "243"
41653       ],
41654       [
41655         "Congo (Republic) (Congo-Brazzaville)",
41656         "cg",
41657         "242"
41658       ],
41659       [
41660         "Cook Islands",
41661         "ck",
41662         "682"
41663       ],
41664       [
41665         "Costa Rica",
41666         "cr",
41667         "506"
41668       ],
41669       [
41670         "Côte d’Ivoire",
41671         "ci",
41672         "225"
41673       ],
41674       [
41675         "Croatia (Hrvatska)",
41676         "hr",
41677         "385"
41678       ],
41679       [
41680         "Cuba",
41681         "cu",
41682         "53"
41683       ],
41684       [
41685         "Curaçao",
41686         "cw",
41687         "599",
41688         0
41689       ],
41690       [
41691         "Cyprus (Κύπρος)",
41692         "cy",
41693         "357"
41694       ],
41695       [
41696         "Czech Republic (Česká republika)",
41697         "cz",
41698         "420"
41699       ],
41700       [
41701         "Denmark (Danmark)",
41702         "dk",
41703         "45"
41704       ],
41705       [
41706         "Djibouti",
41707         "dj",
41708         "253"
41709       ],
41710       [
41711         "Dominica",
41712         "dm",
41713         "1767"
41714       ],
41715       [
41716         "Dominican Republic (República Dominicana)",
41717         "do",
41718         "1",
41719         2,
41720         ["809", "829", "849"]
41721       ],
41722       [
41723         "Ecuador",
41724         "ec",
41725         "593"
41726       ],
41727       [
41728         "Egypt (‫مصر‬‎)",
41729         "eg",
41730         "20"
41731       ],
41732       [
41733         "El Salvador",
41734         "sv",
41735         "503"
41736       ],
41737       [
41738         "Equatorial Guinea (Guinea Ecuatorial)",
41739         "gq",
41740         "240"
41741       ],
41742       [
41743         "Eritrea",
41744         "er",
41745         "291"
41746       ],
41747       [
41748         "Estonia (Eesti)",
41749         "ee",
41750         "372"
41751       ],
41752       [
41753         "Ethiopia",
41754         "et",
41755         "251"
41756       ],
41757       [
41758         "Falkland Islands (Islas Malvinas)",
41759         "fk",
41760         "500"
41761       ],
41762       [
41763         "Faroe Islands (Føroyar)",
41764         "fo",
41765         "298"
41766       ],
41767       [
41768         "Fiji",
41769         "fj",
41770         "679"
41771       ],
41772       [
41773         "Finland (Suomi)",
41774         "fi",
41775         "358",
41776         0
41777       ],
41778       [
41779         "France",
41780         "fr",
41781         "33"
41782       ],
41783       [
41784         "French Guiana (Guyane française)",
41785         "gf",
41786         "594"
41787       ],
41788       [
41789         "French Polynesia (Polynésie française)",
41790         "pf",
41791         "689"
41792       ],
41793       [
41794         "Gabon",
41795         "ga",
41796         "241"
41797       ],
41798       [
41799         "Gambia",
41800         "gm",
41801         "220"
41802       ],
41803       [
41804         "Georgia (საქართველო)",
41805         "ge",
41806         "995"
41807       ],
41808       [
41809         "Germany (Deutschland)",
41810         "de",
41811         "49"
41812       ],
41813       [
41814         "Ghana (Gaana)",
41815         "gh",
41816         "233"
41817       ],
41818       [
41819         "Gibraltar",
41820         "gi",
41821         "350"
41822       ],
41823       [
41824         "Greece (Ελλάδα)",
41825         "gr",
41826         "30"
41827       ],
41828       [
41829         "Greenland (Kalaallit Nunaat)",
41830         "gl",
41831         "299"
41832       ],
41833       [
41834         "Grenada",
41835         "gd",
41836         "1473"
41837       ],
41838       [
41839         "Guadeloupe",
41840         "gp",
41841         "590",
41842         0
41843       ],
41844       [
41845         "Guam",
41846         "gu",
41847         "1671"
41848       ],
41849       [
41850         "Guatemala",
41851         "gt",
41852         "502"
41853       ],
41854       [
41855         "Guernsey",
41856         "gg",
41857         "44",
41858         1
41859       ],
41860       [
41861         "Guinea (Guinée)",
41862         "gn",
41863         "224"
41864       ],
41865       [
41866         "Guinea-Bissau (Guiné Bissau)",
41867         "gw",
41868         "245"
41869       ],
41870       [
41871         "Guyana",
41872         "gy",
41873         "592"
41874       ],
41875       [
41876         "Haiti",
41877         "ht",
41878         "509"
41879       ],
41880       [
41881         "Honduras",
41882         "hn",
41883         "504"
41884       ],
41885       [
41886         "Hong Kong (香港)",
41887         "hk",
41888         "852"
41889       ],
41890       [
41891         "Hungary (Magyarország)",
41892         "hu",
41893         "36"
41894       ],
41895       [
41896         "Iceland (Ísland)",
41897         "is",
41898         "354"
41899       ],
41900       [
41901         "India (भारत)",
41902         "in",
41903         "91"
41904       ],
41905       [
41906         "Indonesia",
41907         "id",
41908         "62"
41909       ],
41910       [
41911         "Iran (‫ایران‬‎)",
41912         "ir",
41913         "98"
41914       ],
41915       [
41916         "Iraq (‫العراق‬‎)",
41917         "iq",
41918         "964"
41919       ],
41920       [
41921         "Ireland",
41922         "ie",
41923         "353"
41924       ],
41925       [
41926         "Isle of Man",
41927         "im",
41928         "44",
41929         2
41930       ],
41931       [
41932         "Israel (‫ישראל‬‎)",
41933         "il",
41934         "972"
41935       ],
41936       [
41937         "Italy (Italia)",
41938         "it",
41939         "39",
41940         0
41941       ],
41942       [
41943         "Jamaica",
41944         "jm",
41945         "1876"
41946       ],
41947       [
41948         "Japan (日本)",
41949         "jp",
41950         "81"
41951       ],
41952       [
41953         "Jersey",
41954         "je",
41955         "44",
41956         3
41957       ],
41958       [
41959         "Jordan (‫الأردن‬‎)",
41960         "jo",
41961         "962"
41962       ],
41963       [
41964         "Kazakhstan (Казахстан)",
41965         "kz",
41966         "7",
41967         1
41968       ],
41969       [
41970         "Kenya",
41971         "ke",
41972         "254"
41973       ],
41974       [
41975         "Kiribati",
41976         "ki",
41977         "686"
41978       ],
41979       [
41980         "Kosovo",
41981         "xk",
41982         "383"
41983       ],
41984       [
41985         "Kuwait (‫الكويت‬‎)",
41986         "kw",
41987         "965"
41988       ],
41989       [
41990         "Kyrgyzstan (Кыргызстан)",
41991         "kg",
41992         "996"
41993       ],
41994       [
41995         "Laos (ລາວ)",
41996         "la",
41997         "856"
41998       ],
41999       [
42000         "Latvia (Latvija)",
42001         "lv",
42002         "371"
42003       ],
42004       [
42005         "Lebanon (‫لبنان‬‎)",
42006         "lb",
42007         "961"
42008       ],
42009       [
42010         "Lesotho",
42011         "ls",
42012         "266"
42013       ],
42014       [
42015         "Liberia",
42016         "lr",
42017         "231"
42018       ],
42019       [
42020         "Libya (‫ليبيا‬‎)",
42021         "ly",
42022         "218"
42023       ],
42024       [
42025         "Liechtenstein",
42026         "li",
42027         "423"
42028       ],
42029       [
42030         "Lithuania (Lietuva)",
42031         "lt",
42032         "370"
42033       ],
42034       [
42035         "Luxembourg",
42036         "lu",
42037         "352"
42038       ],
42039       [
42040         "Macau (澳門)",
42041         "mo",
42042         "853"
42043       ],
42044       [
42045         "Macedonia (FYROM) (Македонија)",
42046         "mk",
42047         "389"
42048       ],
42049       [
42050         "Madagascar (Madagasikara)",
42051         "mg",
42052         "261"
42053       ],
42054       [
42055         "Malawi",
42056         "mw",
42057         "265"
42058       ],
42059       [
42060         "Malaysia",
42061         "my",
42062         "60"
42063       ],
42064       [
42065         "Maldives",
42066         "mv",
42067         "960"
42068       ],
42069       [
42070         "Mali",
42071         "ml",
42072         "223"
42073       ],
42074       [
42075         "Malta",
42076         "mt",
42077         "356"
42078       ],
42079       [
42080         "Marshall Islands",
42081         "mh",
42082         "692"
42083       ],
42084       [
42085         "Martinique",
42086         "mq",
42087         "596"
42088       ],
42089       [
42090         "Mauritania (‫موريتانيا‬‎)",
42091         "mr",
42092         "222"
42093       ],
42094       [
42095         "Mauritius (Moris)",
42096         "mu",
42097         "230"
42098       ],
42099       [
42100         "Mayotte",
42101         "yt",
42102         "262",
42103         1
42104       ],
42105       [
42106         "Mexico (México)",
42107         "mx",
42108         "52"
42109       ],
42110       [
42111         "Micronesia",
42112         "fm",
42113         "691"
42114       ],
42115       [
42116         "Moldova (Republica Moldova)",
42117         "md",
42118         "373"
42119       ],
42120       [
42121         "Monaco",
42122         "mc",
42123         "377"
42124       ],
42125       [
42126         "Mongolia (Монгол)",
42127         "mn",
42128         "976"
42129       ],
42130       [
42131         "Montenegro (Crna Gora)",
42132         "me",
42133         "382"
42134       ],
42135       [
42136         "Montserrat",
42137         "ms",
42138         "1664"
42139       ],
42140       [
42141         "Morocco (‫المغرب‬‎)",
42142         "ma",
42143         "212",
42144         0
42145       ],
42146       [
42147         "Mozambique (Moçambique)",
42148         "mz",
42149         "258"
42150       ],
42151       [
42152         "Myanmar (Burma) (မြန်မာ)",
42153         "mm",
42154         "95"
42155       ],
42156       [
42157         "Namibia (Namibië)",
42158         "na",
42159         "264"
42160       ],
42161       [
42162         "Nauru",
42163         "nr",
42164         "674"
42165       ],
42166       [
42167         "Nepal (नेपाल)",
42168         "np",
42169         "977"
42170       ],
42171       [
42172         "Netherlands (Nederland)",
42173         "nl",
42174         "31"
42175       ],
42176       [
42177         "New Caledonia (Nouvelle-Calédonie)",
42178         "nc",
42179         "687"
42180       ],
42181       [
42182         "New Zealand",
42183         "nz",
42184         "64"
42185       ],
42186       [
42187         "Nicaragua",
42188         "ni",
42189         "505"
42190       ],
42191       [
42192         "Niger (Nijar)",
42193         "ne",
42194         "227"
42195       ],
42196       [
42197         "Nigeria",
42198         "ng",
42199         "234"
42200       ],
42201       [
42202         "Niue",
42203         "nu",
42204         "683"
42205       ],
42206       [
42207         "Norfolk Island",
42208         "nf",
42209         "672"
42210       ],
42211       [
42212         "North Korea (조선 민주주의 인민 공화국)",
42213         "kp",
42214         "850"
42215       ],
42216       [
42217         "Northern Mariana Islands",
42218         "mp",
42219         "1670"
42220       ],
42221       [
42222         "Norway (Norge)",
42223         "no",
42224         "47",
42225         0
42226       ],
42227       [
42228         "Oman (‫عُمان‬‎)",
42229         "om",
42230         "968"
42231       ],
42232       [
42233         "Pakistan (‫پاکستان‬‎)",
42234         "pk",
42235         "92"
42236       ],
42237       [
42238         "Palau",
42239         "pw",
42240         "680"
42241       ],
42242       [
42243         "Palestine (‫فلسطين‬‎)",
42244         "ps",
42245         "970"
42246       ],
42247       [
42248         "Panama (Panamá)",
42249         "pa",
42250         "507"
42251       ],
42252       [
42253         "Papua New Guinea",
42254         "pg",
42255         "675"
42256       ],
42257       [
42258         "Paraguay",
42259         "py",
42260         "595"
42261       ],
42262       [
42263         "Peru (Perú)",
42264         "pe",
42265         "51"
42266       ],
42267       [
42268         "Philippines",
42269         "ph",
42270         "63"
42271       ],
42272       [
42273         "Poland (Polska)",
42274         "pl",
42275         "48"
42276       ],
42277       [
42278         "Portugal",
42279         "pt",
42280         "351"
42281       ],
42282       [
42283         "Puerto Rico",
42284         "pr",
42285         "1",
42286         3,
42287         ["787", "939"]
42288       ],
42289       [
42290         "Qatar (‫قطر‬‎)",
42291         "qa",
42292         "974"
42293       ],
42294       [
42295         "Réunion (La Réunion)",
42296         "re",
42297         "262",
42298         0
42299       ],
42300       [
42301         "Romania (România)",
42302         "ro",
42303         "40"
42304       ],
42305       [
42306         "Russia (Россия)",
42307         "ru",
42308         "7",
42309         0
42310       ],
42311       [
42312         "Rwanda",
42313         "rw",
42314         "250"
42315       ],
42316       [
42317         "Saint Barthélemy",
42318         "bl",
42319         "590",
42320         1
42321       ],
42322       [
42323         "Saint Helena",
42324         "sh",
42325         "290"
42326       ],
42327       [
42328         "Saint Kitts and Nevis",
42329         "kn",
42330         "1869"
42331       ],
42332       [
42333         "Saint Lucia",
42334         "lc",
42335         "1758"
42336       ],
42337       [
42338         "Saint Martin (Saint-Martin (partie française))",
42339         "mf",
42340         "590",
42341         2
42342       ],
42343       [
42344         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42345         "pm",
42346         "508"
42347       ],
42348       [
42349         "Saint Vincent and the Grenadines",
42350         "vc",
42351         "1784"
42352       ],
42353       [
42354         "Samoa",
42355         "ws",
42356         "685"
42357       ],
42358       [
42359         "San Marino",
42360         "sm",
42361         "378"
42362       ],
42363       [
42364         "São Tomé and Príncipe (São Tomé e Príncipe)",
42365         "st",
42366         "239"
42367       ],
42368       [
42369         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42370         "sa",
42371         "966"
42372       ],
42373       [
42374         "Senegal (Sénégal)",
42375         "sn",
42376         "221"
42377       ],
42378       [
42379         "Serbia (Србија)",
42380         "rs",
42381         "381"
42382       ],
42383       [
42384         "Seychelles",
42385         "sc",
42386         "248"
42387       ],
42388       [
42389         "Sierra Leone",
42390         "sl",
42391         "232"
42392       ],
42393       [
42394         "Singapore",
42395         "sg",
42396         "65"
42397       ],
42398       [
42399         "Sint Maarten",
42400         "sx",
42401         "1721"
42402       ],
42403       [
42404         "Slovakia (Slovensko)",
42405         "sk",
42406         "421"
42407       ],
42408       [
42409         "Slovenia (Slovenija)",
42410         "si",
42411         "386"
42412       ],
42413       [
42414         "Solomon Islands",
42415         "sb",
42416         "677"
42417       ],
42418       [
42419         "Somalia (Soomaaliya)",
42420         "so",
42421         "252"
42422       ],
42423       [
42424         "South Africa",
42425         "za",
42426         "27"
42427       ],
42428       [
42429         "South Korea (대한민국)",
42430         "kr",
42431         "82"
42432       ],
42433       [
42434         "South Sudan (‫جنوب السودان‬‎)",
42435         "ss",
42436         "211"
42437       ],
42438       [
42439         "Spain (España)",
42440         "es",
42441         "34"
42442       ],
42443       [
42444         "Sri Lanka (ශ්‍රී ලංකාව)",
42445         "lk",
42446         "94"
42447       ],
42448       [
42449         "Sudan (‫السودان‬‎)",
42450         "sd",
42451         "249"
42452       ],
42453       [
42454         "Suriname",
42455         "sr",
42456         "597"
42457       ],
42458       [
42459         "Svalbard and Jan Mayen",
42460         "sj",
42461         "47",
42462         1
42463       ],
42464       [
42465         "Swaziland",
42466         "sz",
42467         "268"
42468       ],
42469       [
42470         "Sweden (Sverige)",
42471         "se",
42472         "46"
42473       ],
42474       [
42475         "Switzerland (Schweiz)",
42476         "ch",
42477         "41"
42478       ],
42479       [
42480         "Syria (‫سوريا‬‎)",
42481         "sy",
42482         "963"
42483       ],
42484       [
42485         "Taiwan (台灣)",
42486         "tw",
42487         "886"
42488       ],
42489       [
42490         "Tajikistan",
42491         "tj",
42492         "992"
42493       ],
42494       [
42495         "Tanzania",
42496         "tz",
42497         "255"
42498       ],
42499       [
42500         "Thailand (ไทย)",
42501         "th",
42502         "66"
42503       ],
42504       [
42505         "Timor-Leste",
42506         "tl",
42507         "670"
42508       ],
42509       [
42510         "Togo",
42511         "tg",
42512         "228"
42513       ],
42514       [
42515         "Tokelau",
42516         "tk",
42517         "690"
42518       ],
42519       [
42520         "Tonga",
42521         "to",
42522         "676"
42523       ],
42524       [
42525         "Trinidad and Tobago",
42526         "tt",
42527         "1868"
42528       ],
42529       [
42530         "Tunisia (‫تونس‬‎)",
42531         "tn",
42532         "216"
42533       ],
42534       [
42535         "Turkey (Türkiye)",
42536         "tr",
42537         "90"
42538       ],
42539       [
42540         "Turkmenistan",
42541         "tm",
42542         "993"
42543       ],
42544       [
42545         "Turks and Caicos Islands",
42546         "tc",
42547         "1649"
42548       ],
42549       [
42550         "Tuvalu",
42551         "tv",
42552         "688"
42553       ],
42554       [
42555         "U.S. Virgin Islands",
42556         "vi",
42557         "1340"
42558       ],
42559       [
42560         "Uganda",
42561         "ug",
42562         "256"
42563       ],
42564       [
42565         "Ukraine (Україна)",
42566         "ua",
42567         "380"
42568       ],
42569       [
42570         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42571         "ae",
42572         "971"
42573       ],
42574       [
42575         "United Kingdom",
42576         "gb",
42577         "44",
42578         0
42579       ],
42580       [
42581         "United States",
42582         "us",
42583         "1",
42584         0
42585       ],
42586       [
42587         "Uruguay",
42588         "uy",
42589         "598"
42590       ],
42591       [
42592         "Uzbekistan (Oʻzbekiston)",
42593         "uz",
42594         "998"
42595       ],
42596       [
42597         "Vanuatu",
42598         "vu",
42599         "678"
42600       ],
42601       [
42602         "Vatican City (Città del Vaticano)",
42603         "va",
42604         "39",
42605         1
42606       ],
42607       [
42608         "Venezuela",
42609         "ve",
42610         "58"
42611       ],
42612       [
42613         "Vietnam (Việt Nam)",
42614         "vn",
42615         "84"
42616       ],
42617       [
42618         "Wallis and Futuna (Wallis-et-Futuna)",
42619         "wf",
42620         "681"
42621       ],
42622       [
42623         "Western Sahara (‫الصحراء الغربية‬‎)",
42624         "eh",
42625         "212",
42626         1
42627       ],
42628       [
42629         "Yemen (‫اليمن‬‎)",
42630         "ye",
42631         "967"
42632       ],
42633       [
42634         "Zambia",
42635         "zm",
42636         "260"
42637       ],
42638       [
42639         "Zimbabwe",
42640         "zw",
42641         "263"
42642       ],
42643       [
42644         "Åland Islands",
42645         "ax",
42646         "358",
42647         1
42648       ]
42649   ];
42650   
42651   return d;
42652 }/**
42653 *    This script refer to:
42654 *    Title: International Telephone Input
42655 *    Author: Jack O'Connor
42656 *    Code version:  v12.1.12
42657 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42658 **/
42659
42660 /**
42661  * @class Roo.bootstrap.PhoneInput
42662  * @extends Roo.bootstrap.TriggerField
42663  * An input with International dial-code selection
42664  
42665  * @cfg {String} defaultDialCode default '+852'
42666  * @cfg {Array} preferedCountries default []
42667   
42668  * @constructor
42669  * Create a new PhoneInput.
42670  * @param {Object} config Configuration options
42671  */
42672
42673 Roo.bootstrap.PhoneInput = function(config) {
42674     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42675 };
42676
42677 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42678         
42679         listWidth: undefined,
42680         
42681         selectedClass: 'active',
42682         
42683         invalidClass : "has-warning",
42684         
42685         validClass: 'has-success',
42686         
42687         allowed: '0123456789',
42688         
42689         max_length: 15,
42690         
42691         /**
42692          * @cfg {String} defaultDialCode The default dial code when initializing the input
42693          */
42694         defaultDialCode: '+852',
42695         
42696         /**
42697          * @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
42698          */
42699         preferedCountries: false,
42700         
42701         getAutoCreate : function()
42702         {
42703             var data = Roo.bootstrap.PhoneInputData();
42704             var align = this.labelAlign || this.parentLabelAlign();
42705             var id = Roo.id();
42706             
42707             this.allCountries = [];
42708             this.dialCodeMapping = [];
42709             
42710             for (var i = 0; i < data.length; i++) {
42711               var c = data[i];
42712               this.allCountries[i] = {
42713                 name: c[0],
42714                 iso2: c[1],
42715                 dialCode: c[2],
42716                 priority: c[3] || 0,
42717                 areaCodes: c[4] || null
42718               };
42719               this.dialCodeMapping[c[2]] = {
42720                   name: c[0],
42721                   iso2: c[1],
42722                   priority: c[3] || 0,
42723                   areaCodes: c[4] || null
42724               };
42725             }
42726             
42727             var cfg = {
42728                 cls: 'form-group',
42729                 cn: []
42730             };
42731             
42732             var input =  {
42733                 tag: 'input',
42734                 id : id,
42735                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42736                 maxlength: this.max_length,
42737                 cls : 'form-control tel-input',
42738                 autocomplete: 'new-password'
42739             };
42740             
42741             var hiddenInput = {
42742                 tag: 'input',
42743                 type: 'hidden',
42744                 cls: 'hidden-tel-input'
42745             };
42746             
42747             if (this.name) {
42748                 hiddenInput.name = this.name;
42749             }
42750             
42751             if (this.disabled) {
42752                 input.disabled = true;
42753             }
42754             
42755             var flag_container = {
42756                 tag: 'div',
42757                 cls: 'flag-box',
42758                 cn: [
42759                     {
42760                         tag: 'div',
42761                         cls: 'flag'
42762                     },
42763                     {
42764                         tag: 'div',
42765                         cls: 'caret'
42766                     }
42767                 ]
42768             };
42769             
42770             var box = {
42771                 tag: 'div',
42772                 cls: this.hasFeedback ? 'has-feedback' : '',
42773                 cn: [
42774                     hiddenInput,
42775                     input,
42776                     {
42777                         tag: 'input',
42778                         cls: 'dial-code-holder',
42779                         disabled: true
42780                     }
42781                 ]
42782             };
42783             
42784             var container = {
42785                 cls: 'roo-select2-container input-group',
42786                 cn: [
42787                     flag_container,
42788                     box
42789                 ]
42790             };
42791             
42792             if (this.fieldLabel.length) {
42793                 var indicator = {
42794                     tag: 'i',
42795                     tooltip: 'This field is required'
42796                 };
42797                 
42798                 var label = {
42799                     tag: 'label',
42800                     'for':  id,
42801                     cls: 'control-label',
42802                     cn: []
42803                 };
42804                 
42805                 var label_text = {
42806                     tag: 'span',
42807                     html: this.fieldLabel
42808                 };
42809                 
42810                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42811                 label.cn = [
42812                     indicator,
42813                     label_text
42814                 ];
42815                 
42816                 if(this.indicatorpos == 'right') {
42817                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42818                     label.cn = [
42819                         label_text,
42820                         indicator
42821                     ];
42822                 }
42823                 
42824                 if(align == 'left') {
42825                     container = {
42826                         tag: 'div',
42827                         cn: [
42828                             container
42829                         ]
42830                     };
42831                     
42832                     if(this.labelWidth > 12){
42833                         label.style = "width: " + this.labelWidth + 'px';
42834                     }
42835                     if(this.labelWidth < 13 && this.labelmd == 0){
42836                         this.labelmd = this.labelWidth;
42837                     }
42838                     if(this.labellg > 0){
42839                         label.cls += ' col-lg-' + this.labellg;
42840                         input.cls += ' col-lg-' + (12 - this.labellg);
42841                     }
42842                     if(this.labelmd > 0){
42843                         label.cls += ' col-md-' + this.labelmd;
42844                         container.cls += ' col-md-' + (12 - this.labelmd);
42845                     }
42846                     if(this.labelsm > 0){
42847                         label.cls += ' col-sm-' + this.labelsm;
42848                         container.cls += ' col-sm-' + (12 - this.labelsm);
42849                     }
42850                     if(this.labelxs > 0){
42851                         label.cls += ' col-xs-' + this.labelxs;
42852                         container.cls += ' col-xs-' + (12 - this.labelxs);
42853                     }
42854                 }
42855             }
42856             
42857             cfg.cn = [
42858                 label,
42859                 container
42860             ];
42861             
42862             var settings = this;
42863             
42864             ['xs','sm','md','lg'].map(function(size){
42865                 if (settings[size]) {
42866                     cfg.cls += ' col-' + size + '-' + settings[size];
42867                 }
42868             });
42869             
42870             this.store = new Roo.data.Store({
42871                 proxy : new Roo.data.MemoryProxy({}),
42872                 reader : new Roo.data.JsonReader({
42873                     fields : [
42874                         {
42875                             'name' : 'name',
42876                             'type' : 'string'
42877                         },
42878                         {
42879                             'name' : 'iso2',
42880                             'type' : 'string'
42881                         },
42882                         {
42883                             'name' : 'dialCode',
42884                             'type' : 'string'
42885                         },
42886                         {
42887                             'name' : 'priority',
42888                             'type' : 'string'
42889                         },
42890                         {
42891                             'name' : 'areaCodes',
42892                             'type' : 'string'
42893                         }
42894                     ]
42895                 })
42896             });
42897             
42898             if(!this.preferedCountries) {
42899                 this.preferedCountries = [
42900                     'hk',
42901                     'gb',
42902                     'us'
42903                 ];
42904             }
42905             
42906             var p = this.preferedCountries.reverse();
42907             
42908             if(p) {
42909                 for (var i = 0; i < p.length; i++) {
42910                     for (var j = 0; j < this.allCountries.length; j++) {
42911                         if(this.allCountries[j].iso2 == p[i]) {
42912                             var t = this.allCountries[j];
42913                             this.allCountries.splice(j,1);
42914                             this.allCountries.unshift(t);
42915                         }
42916                     } 
42917                 }
42918             }
42919             
42920             this.store.proxy.data = {
42921                 success: true,
42922                 data: this.allCountries
42923             };
42924             
42925             return cfg;
42926         },
42927         
42928         initEvents : function()
42929         {
42930             this.createList();
42931             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42932             
42933             this.indicator = this.indicatorEl();
42934             this.flag = this.flagEl();
42935             this.dialCodeHolder = this.dialCodeHolderEl();
42936             
42937             this.trigger = this.el.select('div.flag-box',true).first();
42938             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42939             
42940             var _this = this;
42941             
42942             (function(){
42943                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42944                 _this.list.setWidth(lw);
42945             }).defer(100);
42946             
42947             this.list.on('mouseover', this.onViewOver, this);
42948             this.list.on('mousemove', this.onViewMove, this);
42949             this.inputEl().on("keyup", this.onKeyUp, this);
42950             this.inputEl().on("keypress", this.onKeyPress, this);
42951             
42952             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42953
42954             this.view = new Roo.View(this.list, this.tpl, {
42955                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42956             });
42957             
42958             this.view.on('click', this.onViewClick, this);
42959             this.setValue(this.defaultDialCode);
42960         },
42961         
42962         onTriggerClick : function(e)
42963         {
42964             Roo.log('trigger click');
42965             if(this.disabled){
42966                 return;
42967             }
42968             
42969             if(this.isExpanded()){
42970                 this.collapse();
42971                 this.hasFocus = false;
42972             }else {
42973                 this.store.load({});
42974                 this.hasFocus = true;
42975                 this.expand();
42976             }
42977         },
42978         
42979         isExpanded : function()
42980         {
42981             return this.list.isVisible();
42982         },
42983         
42984         collapse : function()
42985         {
42986             if(!this.isExpanded()){
42987                 return;
42988             }
42989             this.list.hide();
42990             Roo.get(document).un('mousedown', this.collapseIf, this);
42991             Roo.get(document).un('mousewheel', this.collapseIf, this);
42992             this.fireEvent('collapse', this);
42993             this.validate();
42994         },
42995         
42996         expand : function()
42997         {
42998             Roo.log('expand');
42999
43000             if(this.isExpanded() || !this.hasFocus){
43001                 return;
43002             }
43003             
43004             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
43005             this.list.setWidth(lw);
43006             
43007             this.list.show();
43008             this.restrictHeight();
43009             
43010             Roo.get(document).on('mousedown', this.collapseIf, this);
43011             Roo.get(document).on('mousewheel', this.collapseIf, this);
43012             
43013             this.fireEvent('expand', this);
43014         },
43015         
43016         restrictHeight : function()
43017         {
43018             this.list.alignTo(this.inputEl(), this.listAlign);
43019             this.list.alignTo(this.inputEl(), this.listAlign);
43020         },
43021         
43022         onViewOver : function(e, t)
43023         {
43024             if(this.inKeyMode){
43025                 return;
43026             }
43027             var item = this.view.findItemFromChild(t);
43028             
43029             if(item){
43030                 var index = this.view.indexOf(item);
43031                 this.select(index, false);
43032             }
43033         },
43034
43035         // private
43036         onViewClick : function(view, doFocus, el, e)
43037         {
43038             var index = this.view.getSelectedIndexes()[0];
43039             
43040             var r = this.store.getAt(index);
43041             
43042             if(r){
43043                 this.onSelect(r, index);
43044             }
43045             if(doFocus !== false && !this.blockFocus){
43046                 this.inputEl().focus();
43047             }
43048         },
43049         
43050         onViewMove : function(e, t)
43051         {
43052             this.inKeyMode = false;
43053         },
43054         
43055         select : function(index, scrollIntoView)
43056         {
43057             this.selectedIndex = index;
43058             this.view.select(index);
43059             if(scrollIntoView !== false){
43060                 var el = this.view.getNode(index);
43061                 if(el){
43062                     this.list.scrollChildIntoView(el, false);
43063                 }
43064             }
43065         },
43066         
43067         createList : function()
43068         {
43069             this.list = Roo.get(document.body).createChild({
43070                 tag: 'ul',
43071                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43072                 style: 'display:none'
43073             });
43074             
43075             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43076         },
43077         
43078         collapseIf : function(e)
43079         {
43080             var in_combo  = e.within(this.el);
43081             var in_list =  e.within(this.list);
43082             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43083             
43084             if (in_combo || in_list || is_list) {
43085                 return;
43086             }
43087             this.collapse();
43088         },
43089         
43090         onSelect : function(record, index)
43091         {
43092             if(this.fireEvent('beforeselect', this, record, index) !== false){
43093                 
43094                 this.setFlagClass(record.data.iso2);
43095                 this.setDialCode(record.data.dialCode);
43096                 this.hasFocus = false;
43097                 this.collapse();
43098                 this.fireEvent('select', this, record, index);
43099             }
43100         },
43101         
43102         flagEl : function()
43103         {
43104             var flag = this.el.select('div.flag',true).first();
43105             if(!flag){
43106                 return false;
43107             }
43108             return flag;
43109         },
43110         
43111         dialCodeHolderEl : function()
43112         {
43113             var d = this.el.select('input.dial-code-holder',true).first();
43114             if(!d){
43115                 return false;
43116             }
43117             return d;
43118         },
43119         
43120         setDialCode : function(v)
43121         {
43122             this.dialCodeHolder.dom.value = '+'+v;
43123         },
43124         
43125         setFlagClass : function(n)
43126         {
43127             this.flag.dom.className = 'flag '+n;
43128         },
43129         
43130         getValue : function()
43131         {
43132             var v = this.inputEl().getValue();
43133             if(this.dialCodeHolder) {
43134                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43135             }
43136             return v;
43137         },
43138         
43139         setValue : function(v)
43140         {
43141             var d = this.getDialCode(v);
43142             
43143             //invalid dial code
43144             if(v.length == 0 || !d || d.length == 0) {
43145                 if(this.rendered){
43146                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43147                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43148                 }
43149                 return;
43150             }
43151             
43152             //valid dial code
43153             this.setFlagClass(this.dialCodeMapping[d].iso2);
43154             this.setDialCode(d);
43155             this.inputEl().dom.value = v.replace('+'+d,'');
43156             this.hiddenEl().dom.value = this.getValue();
43157             
43158             this.validate();
43159         },
43160         
43161         getDialCode : function(v)
43162         {
43163             v = v ||  '';
43164             
43165             if (v.length == 0) {
43166                 return this.dialCodeHolder.dom.value;
43167             }
43168             
43169             var dialCode = "";
43170             if (v.charAt(0) != "+") {
43171                 return false;
43172             }
43173             var numericChars = "";
43174             for (var i = 1; i < v.length; i++) {
43175               var c = v.charAt(i);
43176               if (!isNaN(c)) {
43177                 numericChars += c;
43178                 if (this.dialCodeMapping[numericChars]) {
43179                   dialCode = v.substr(1, i);
43180                 }
43181                 if (numericChars.length == 4) {
43182                   break;
43183                 }
43184               }
43185             }
43186             return dialCode;
43187         },
43188         
43189         reset : function()
43190         {
43191             this.setValue(this.defaultDialCode);
43192             this.validate();
43193         },
43194         
43195         hiddenEl : function()
43196         {
43197             return this.el.select('input.hidden-tel-input',true).first();
43198         },
43199         
43200         // after setting val
43201         onKeyUp : function(e){
43202             this.setValue(this.getValue());
43203         },
43204         
43205         onKeyPress : function(e){
43206             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43207                 e.stopEvent();
43208             }
43209         }
43210         
43211 });
43212 /**
43213  * @class Roo.bootstrap.MoneyField
43214  * @extends Roo.bootstrap.ComboBox
43215  * Bootstrap MoneyField class
43216  * 
43217  * @constructor
43218  * Create a new MoneyField.
43219  * @param {Object} config Configuration options
43220  */
43221
43222 Roo.bootstrap.MoneyField = function(config) {
43223     
43224     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43225     
43226 };
43227
43228 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43229     
43230     /**
43231      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43232      */
43233     allowDecimals : true,
43234     /**
43235      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43236      */
43237     decimalSeparator : ".",
43238     /**
43239      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43240      */
43241     decimalPrecision : 0,
43242     /**
43243      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43244      */
43245     allowNegative : true,
43246     /**
43247      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43248      */
43249     allowZero: true,
43250     /**
43251      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43252      */
43253     minValue : Number.NEGATIVE_INFINITY,
43254     /**
43255      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43256      */
43257     maxValue : Number.MAX_VALUE,
43258     /**
43259      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43260      */
43261     minText : "The minimum value for this field is {0}",
43262     /**
43263      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43264      */
43265     maxText : "The maximum value for this field is {0}",
43266     /**
43267      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43268      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43269      */
43270     nanText : "{0} is not a valid number",
43271     /**
43272      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43273      */
43274     castInt : true,
43275     /**
43276      * @cfg {String} defaults currency of the MoneyField
43277      * value should be in lkey
43278      */
43279     defaultCurrency : false,
43280     /**
43281      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43282      */
43283     thousandsDelimiter : false,
43284     /**
43285      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43286      */
43287     max_length: false,
43288     
43289     inputlg : 9,
43290     inputmd : 9,
43291     inputsm : 9,
43292     inputxs : 6,
43293     
43294     store : false,
43295     
43296     getAutoCreate : function()
43297     {
43298         var align = this.labelAlign || this.parentLabelAlign();
43299         
43300         var id = Roo.id();
43301
43302         var cfg = {
43303             cls: 'form-group',
43304             cn: []
43305         };
43306
43307         var input =  {
43308             tag: 'input',
43309             id : id,
43310             cls : 'form-control roo-money-amount-input',
43311             autocomplete: 'new-password'
43312         };
43313         
43314         var hiddenInput = {
43315             tag: 'input',
43316             type: 'hidden',
43317             id: Roo.id(),
43318             cls: 'hidden-number-input'
43319         };
43320         
43321         if(this.max_length) {
43322             input.maxlength = this.max_length; 
43323         }
43324         
43325         if (this.name) {
43326             hiddenInput.name = this.name;
43327         }
43328
43329         if (this.disabled) {
43330             input.disabled = true;
43331         }
43332
43333         var clg = 12 - this.inputlg;
43334         var cmd = 12 - this.inputmd;
43335         var csm = 12 - this.inputsm;
43336         var cxs = 12 - this.inputxs;
43337         
43338         var container = {
43339             tag : 'div',
43340             cls : 'row roo-money-field',
43341             cn : [
43342                 {
43343                     tag : 'div',
43344                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43345                     cn : [
43346                         {
43347                             tag : 'div',
43348                             cls: 'roo-select2-container input-group',
43349                             cn: [
43350                                 {
43351                                     tag : 'input',
43352                                     cls : 'form-control roo-money-currency-input',
43353                                     autocomplete: 'new-password',
43354                                     readOnly : 1,
43355                                     name : this.currencyName
43356                                 },
43357                                 {
43358                                     tag :'span',
43359                                     cls : 'input-group-addon',
43360                                     cn : [
43361                                         {
43362                                             tag: 'span',
43363                                             cls: 'caret'
43364                                         }
43365                                     ]
43366                                 }
43367                             ]
43368                         }
43369                     ]
43370                 },
43371                 {
43372                     tag : 'div',
43373                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43374                     cn : [
43375                         {
43376                             tag: 'div',
43377                             cls: this.hasFeedback ? 'has-feedback' : '',
43378                             cn: [
43379                                 input
43380                             ]
43381                         }
43382                     ]
43383                 }
43384             ]
43385             
43386         };
43387         
43388         if (this.fieldLabel.length) {
43389             var indicator = {
43390                 tag: 'i',
43391                 tooltip: 'This field is required'
43392             };
43393
43394             var label = {
43395                 tag: 'label',
43396                 'for':  id,
43397                 cls: 'control-label',
43398                 cn: []
43399             };
43400
43401             var label_text = {
43402                 tag: 'span',
43403                 html: this.fieldLabel
43404             };
43405
43406             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43407             label.cn = [
43408                 indicator,
43409                 label_text
43410             ];
43411
43412             if(this.indicatorpos == 'right') {
43413                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43414                 label.cn = [
43415                     label_text,
43416                     indicator
43417                 ];
43418             }
43419
43420             if(align == 'left') {
43421                 container = {
43422                     tag: 'div',
43423                     cn: [
43424                         container
43425                     ]
43426                 };
43427
43428                 if(this.labelWidth > 12){
43429                     label.style = "width: " + this.labelWidth + 'px';
43430                 }
43431                 if(this.labelWidth < 13 && this.labelmd == 0){
43432                     this.labelmd = this.labelWidth;
43433                 }
43434                 if(this.labellg > 0){
43435                     label.cls += ' col-lg-' + this.labellg;
43436                     input.cls += ' col-lg-' + (12 - this.labellg);
43437                 }
43438                 if(this.labelmd > 0){
43439                     label.cls += ' col-md-' + this.labelmd;
43440                     container.cls += ' col-md-' + (12 - this.labelmd);
43441                 }
43442                 if(this.labelsm > 0){
43443                     label.cls += ' col-sm-' + this.labelsm;
43444                     container.cls += ' col-sm-' + (12 - this.labelsm);
43445                 }
43446                 if(this.labelxs > 0){
43447                     label.cls += ' col-xs-' + this.labelxs;
43448                     container.cls += ' col-xs-' + (12 - this.labelxs);
43449                 }
43450             }
43451         }
43452
43453         cfg.cn = [
43454             label,
43455             container,
43456             hiddenInput
43457         ];
43458         
43459         var settings = this;
43460
43461         ['xs','sm','md','lg'].map(function(size){
43462             if (settings[size]) {
43463                 cfg.cls += ' col-' + size + '-' + settings[size];
43464             }
43465         });
43466         
43467         return cfg;
43468     },
43469     
43470     initEvents : function()
43471     {
43472         this.indicator = this.indicatorEl();
43473         
43474         this.initCurrencyEvent();
43475         
43476         this.initNumberEvent();
43477     },
43478     
43479     initCurrencyEvent : function()
43480     {
43481         if (!this.store) {
43482             throw "can not find store for combo";
43483         }
43484         
43485         this.store = Roo.factory(this.store, Roo.data);
43486         this.store.parent = this;
43487         
43488         this.createList();
43489         
43490         this.triggerEl = this.el.select('.input-group-addon', true).first();
43491         
43492         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43493         
43494         var _this = this;
43495         
43496         (function(){
43497             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43498             _this.list.setWidth(lw);
43499         }).defer(100);
43500         
43501         this.list.on('mouseover', this.onViewOver, this);
43502         this.list.on('mousemove', this.onViewMove, this);
43503         this.list.on('scroll', this.onViewScroll, this);
43504         
43505         if(!this.tpl){
43506             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43507         }
43508         
43509         this.view = new Roo.View(this.list, this.tpl, {
43510             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43511         });
43512         
43513         this.view.on('click', this.onViewClick, this);
43514         
43515         this.store.on('beforeload', this.onBeforeLoad, this);
43516         this.store.on('load', this.onLoad, this);
43517         this.store.on('loadexception', this.onLoadException, this);
43518         
43519         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43520             "up" : function(e){
43521                 this.inKeyMode = true;
43522                 this.selectPrev();
43523             },
43524
43525             "down" : function(e){
43526                 if(!this.isExpanded()){
43527                     this.onTriggerClick();
43528                 }else{
43529                     this.inKeyMode = true;
43530                     this.selectNext();
43531                 }
43532             },
43533
43534             "enter" : function(e){
43535                 this.collapse();
43536                 
43537                 if(this.fireEvent("specialkey", this, e)){
43538                     this.onViewClick(false);
43539                 }
43540                 
43541                 return true;
43542             },
43543
43544             "esc" : function(e){
43545                 this.collapse();
43546             },
43547
43548             "tab" : function(e){
43549                 this.collapse();
43550                 
43551                 if(this.fireEvent("specialkey", this, e)){
43552                     this.onViewClick(false);
43553                 }
43554                 
43555                 return true;
43556             },
43557
43558             scope : this,
43559
43560             doRelay : function(foo, bar, hname){
43561                 if(hname == 'down' || this.scope.isExpanded()){
43562                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43563                 }
43564                 return true;
43565             },
43566
43567             forceKeyDown: true
43568         });
43569         
43570         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43571         
43572     },
43573     
43574     initNumberEvent : function(e)
43575     {
43576         this.inputEl().on("keydown" , this.fireKey,  this);
43577         this.inputEl().on("focus", this.onFocus,  this);
43578         this.inputEl().on("blur", this.onBlur,  this);
43579         
43580         this.inputEl().relayEvent('keyup', this);
43581         
43582         if(this.indicator){
43583             this.indicator.addClass('invisible');
43584         }
43585  
43586         this.originalValue = this.getValue();
43587         
43588         if(this.validationEvent == 'keyup'){
43589             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43590             this.inputEl().on('keyup', this.filterValidation, this);
43591         }
43592         else if(this.validationEvent !== false){
43593             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43594         }
43595         
43596         if(this.selectOnFocus){
43597             this.on("focus", this.preFocus, this);
43598             
43599         }
43600         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43601             this.inputEl().on("keypress", this.filterKeys, this);
43602         } else {
43603             this.inputEl().relayEvent('keypress', this);
43604         }
43605         
43606         var allowed = "0123456789";
43607         
43608         if(this.allowDecimals){
43609             allowed += this.decimalSeparator;
43610         }
43611         
43612         if(this.allowNegative){
43613             allowed += "-";
43614         }
43615         
43616         if(this.thousandsDelimiter) {
43617             allowed += ",";
43618         }
43619         
43620         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43621         
43622         var keyPress = function(e){
43623             
43624             var k = e.getKey();
43625             
43626             var c = e.getCharCode();
43627             
43628             if(
43629                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43630                     allowed.indexOf(String.fromCharCode(c)) === -1
43631             ){
43632                 e.stopEvent();
43633                 return;
43634             }
43635             
43636             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43637                 return;
43638             }
43639             
43640             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43641                 e.stopEvent();
43642             }
43643         };
43644         
43645         this.inputEl().on("keypress", keyPress, this);
43646         
43647     },
43648     
43649     onTriggerClick : function(e)
43650     {   
43651         if(this.disabled){
43652             return;
43653         }
43654         
43655         this.page = 0;
43656         this.loadNext = false;
43657         
43658         if(this.isExpanded()){
43659             this.collapse();
43660             return;
43661         }
43662         
43663         this.hasFocus = true;
43664         
43665         if(this.triggerAction == 'all') {
43666             this.doQuery(this.allQuery, true);
43667             return;
43668         }
43669         
43670         this.doQuery(this.getRawValue());
43671     },
43672     
43673     getCurrency : function()
43674     {   
43675         var v = this.currencyEl().getValue();
43676         
43677         return v;
43678     },
43679     
43680     restrictHeight : function()
43681     {
43682         this.list.alignTo(this.currencyEl(), this.listAlign);
43683         this.list.alignTo(this.currencyEl(), this.listAlign);
43684     },
43685     
43686     onViewClick : function(view, doFocus, el, e)
43687     {
43688         var index = this.view.getSelectedIndexes()[0];
43689         
43690         var r = this.store.getAt(index);
43691         
43692         if(r){
43693             this.onSelect(r, index);
43694         }
43695     },
43696     
43697     onSelect : function(record, index){
43698         
43699         if(this.fireEvent('beforeselect', this, record, index) !== false){
43700         
43701             this.setFromCurrencyData(index > -1 ? record.data : false);
43702             
43703             this.collapse();
43704             
43705             this.fireEvent('select', this, record, index);
43706         }
43707     },
43708     
43709     setFromCurrencyData : function(o)
43710     {
43711         var currency = '';
43712         
43713         this.lastCurrency = o;
43714         
43715         if (this.currencyField) {
43716             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43717         } else {
43718             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43719         }
43720         
43721         this.lastSelectionText = currency;
43722         
43723         //setting default currency
43724         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43725             this.setCurrency(this.defaultCurrency);
43726             return;
43727         }
43728         
43729         this.setCurrency(currency);
43730     },
43731     
43732     setFromData : function(o)
43733     {
43734         var c = {};
43735         
43736         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43737         
43738         this.setFromCurrencyData(c);
43739         
43740         var value = '';
43741         
43742         if (this.name) {
43743             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43744         } else {
43745             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43746         }
43747         
43748         this.setValue(value);
43749         
43750     },
43751     
43752     setCurrency : function(v)
43753     {   
43754         this.currencyValue = v;
43755         
43756         if(this.rendered){
43757             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43758             this.validate();
43759         }
43760     },
43761     
43762     setValue : function(v)
43763     {
43764         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43765         
43766         this.value = v;
43767         
43768         if(this.rendered){
43769             
43770             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43771             
43772             this.inputEl().dom.value = (v == '') ? '' :
43773                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43774             
43775             if(!this.allowZero && v === '0') {
43776                 this.hiddenEl().dom.value = '';
43777                 this.inputEl().dom.value = '';
43778             }
43779             
43780             this.validate();
43781         }
43782     },
43783     
43784     getRawValue : function()
43785     {
43786         var v = this.inputEl().getValue();
43787         
43788         return v;
43789     },
43790     
43791     getValue : function()
43792     {
43793         return this.fixPrecision(this.parseValue(this.getRawValue()));
43794     },
43795     
43796     parseValue : function(value)
43797     {
43798         if(this.thousandsDelimiter) {
43799             value += "";
43800             r = new RegExp(",", "g");
43801             value = value.replace(r, "");
43802         }
43803         
43804         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43805         return isNaN(value) ? '' : value;
43806         
43807     },
43808     
43809     fixPrecision : function(value)
43810     {
43811         if(this.thousandsDelimiter) {
43812             value += "";
43813             r = new RegExp(",", "g");
43814             value = value.replace(r, "");
43815         }
43816         
43817         var nan = isNaN(value);
43818         
43819         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43820             return nan ? '' : value;
43821         }
43822         return parseFloat(value).toFixed(this.decimalPrecision);
43823     },
43824     
43825     decimalPrecisionFcn : function(v)
43826     {
43827         return Math.floor(v);
43828     },
43829     
43830     validateValue : function(value)
43831     {
43832         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43833             return false;
43834         }
43835         
43836         var num = this.parseValue(value);
43837         
43838         if(isNaN(num)){
43839             this.markInvalid(String.format(this.nanText, value));
43840             return false;
43841         }
43842         
43843         if(num < this.minValue){
43844             this.markInvalid(String.format(this.minText, this.minValue));
43845             return false;
43846         }
43847         
43848         if(num > this.maxValue){
43849             this.markInvalid(String.format(this.maxText, this.maxValue));
43850             return false;
43851         }
43852         
43853         return true;
43854     },
43855     
43856     validate : function()
43857     {
43858         if(this.disabled || this.allowBlank){
43859             this.markValid();
43860             return true;
43861         }
43862         
43863         var currency = this.getCurrency();
43864         
43865         if(this.validateValue(this.getRawValue()) && currency.length){
43866             this.markValid();
43867             return true;
43868         }
43869         
43870         this.markInvalid();
43871         return false;
43872     },
43873     
43874     getName: function()
43875     {
43876         return this.name;
43877     },
43878     
43879     beforeBlur : function()
43880     {
43881         if(!this.castInt){
43882             return;
43883         }
43884         
43885         var v = this.parseValue(this.getRawValue());
43886         
43887         if(v || v == 0){
43888             this.setValue(v);
43889         }
43890     },
43891     
43892     onBlur : function()
43893     {
43894         this.beforeBlur();
43895         
43896         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43897             //this.el.removeClass(this.focusClass);
43898         }
43899         
43900         this.hasFocus = false;
43901         
43902         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43903             this.validate();
43904         }
43905         
43906         var v = this.getValue();
43907         
43908         if(String(v) !== String(this.startValue)){
43909             this.fireEvent('change', this, v, this.startValue);
43910         }
43911         
43912         this.fireEvent("blur", this);
43913     },
43914     
43915     inputEl : function()
43916     {
43917         return this.el.select('.roo-money-amount-input', true).first();
43918     },
43919     
43920     currencyEl : function()
43921     {
43922         return this.el.select('.roo-money-currency-input', true).first();
43923     },
43924     
43925     hiddenEl : function()
43926     {
43927         return this.el.select('input.hidden-number-input',true).first();
43928     }
43929     
43930 });/**
43931  * @class Roo.bootstrap.BezierSignature
43932  * @extends Roo.bootstrap.Component
43933  * Bootstrap BezierSignature class
43934  * This script refer to:
43935  *    Title: Signature Pad
43936  *    Author: szimek
43937  *    Availability: https://github.com/szimek/signature_pad
43938  *
43939  * @constructor
43940  * Create a new BezierSignature
43941  * @param {Object} config The config object
43942  */
43943
43944 Roo.bootstrap.BezierSignature = function(config){
43945     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43946     this.addEvents({
43947         "resize" : true
43948     });
43949 };
43950
43951 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43952 {
43953      
43954     curve_data: [],
43955     
43956     is_empty: true,
43957     
43958     mouse_btn_down: true,
43959     
43960     /**
43961      * @cfg {int} canvas height
43962      */
43963     canvas_height: '200px',
43964     
43965     /**
43966      * @cfg {float|function} Radius of a single dot.
43967      */ 
43968     dot_size: false,
43969     
43970     /**
43971      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43972      */
43973     min_width: 0.5,
43974     
43975     /**
43976      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43977      */
43978     max_width: 2.5,
43979     
43980     /**
43981      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43982      */
43983     throttle: 16,
43984     
43985     /**
43986      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43987      */
43988     min_distance: 5,
43989     
43990     /**
43991      * @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.
43992      */
43993     bg_color: 'rgba(0, 0, 0, 0)',
43994     
43995     /**
43996      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43997      */
43998     dot_color: 'black',
43999     
44000     /**
44001      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
44002      */ 
44003     velocity_filter_weight: 0.7,
44004     
44005     /**
44006      * @cfg {function} Callback when stroke begin. 
44007      */
44008     onBegin: false,
44009     
44010     /**
44011      * @cfg {function} Callback when stroke end.
44012      */
44013     onEnd: false,
44014     
44015     getAutoCreate : function()
44016     {
44017         var cls = 'roo-signature column';
44018         
44019         if(this.cls){
44020             cls += ' ' + this.cls;
44021         }
44022         
44023         var col_sizes = [
44024             'lg',
44025             'md',
44026             'sm',
44027             'xs'
44028         ];
44029         
44030         for(var i = 0; i < col_sizes.length; i++) {
44031             if(this[col_sizes[i]]) {
44032                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44033             }
44034         }
44035         
44036         var cfg = {
44037             tag: 'div',
44038             cls: cls,
44039             cn: [
44040                 {
44041                     tag: 'div',
44042                     cls: 'roo-signature-body',
44043                     cn: [
44044                         {
44045                             tag: 'canvas',
44046                             cls: 'roo-signature-body-canvas',
44047                             height: this.canvas_height,
44048                             width: this.canvas_width
44049                         }
44050                     ]
44051                 },
44052                 {
44053                     tag: 'input',
44054                     type: 'file',
44055                     style: 'display: none'
44056                 }
44057             ]
44058         };
44059         
44060         return cfg;
44061     },
44062     
44063     initEvents: function() 
44064     {
44065         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44066         
44067         var canvas = this.canvasEl();
44068         
44069         // mouse && touch event swapping...
44070         canvas.dom.style.touchAction = 'none';
44071         canvas.dom.style.msTouchAction = 'none';
44072         
44073         this.mouse_btn_down = false;
44074         canvas.on('mousedown', this._handleMouseDown, this);
44075         canvas.on('mousemove', this._handleMouseMove, this);
44076         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44077         
44078         if (window.PointerEvent) {
44079             canvas.on('pointerdown', this._handleMouseDown, this);
44080             canvas.on('pointermove', this._handleMouseMove, this);
44081             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44082         }
44083         
44084         if ('ontouchstart' in window) {
44085             canvas.on('touchstart', this._handleTouchStart, this);
44086             canvas.on('touchmove', this._handleTouchMove, this);
44087             canvas.on('touchend', this._handleTouchEnd, this);
44088         }
44089         
44090         Roo.EventManager.onWindowResize(this.resize, this, true);
44091         
44092         // file input event
44093         this.fileEl().on('change', this.uploadImage, this);
44094         
44095         this.clear();
44096         
44097         this.resize();
44098     },
44099     
44100     resize: function(){
44101         
44102         var canvas = this.canvasEl().dom;
44103         var ctx = this.canvasElCtx();
44104         var img_data = false;
44105         
44106         if(canvas.width > 0) {
44107             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44108         }
44109         // setting canvas width will clean img data
44110         canvas.width = 0;
44111         
44112         var style = window.getComputedStyle ? 
44113             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44114             
44115         var padding_left = parseInt(style.paddingLeft) || 0;
44116         var padding_right = parseInt(style.paddingRight) || 0;
44117         
44118         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44119         
44120         if(img_data) {
44121             ctx.putImageData(img_data, 0, 0);
44122         }
44123     },
44124     
44125     _handleMouseDown: function(e)
44126     {
44127         if (e.browserEvent.which === 1) {
44128             this.mouse_btn_down = true;
44129             this.strokeBegin(e);
44130         }
44131     },
44132     
44133     _handleMouseMove: function (e)
44134     {
44135         if (this.mouse_btn_down) {
44136             this.strokeMoveUpdate(e);
44137         }
44138     },
44139     
44140     _handleMouseUp: function (e)
44141     {
44142         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44143             this.mouse_btn_down = false;
44144             this.strokeEnd(e);
44145         }
44146     },
44147     
44148     _handleTouchStart: function (e) {
44149         
44150         e.preventDefault();
44151         if (e.browserEvent.targetTouches.length === 1) {
44152             // var touch = e.browserEvent.changedTouches[0];
44153             // this.strokeBegin(touch);
44154             
44155              this.strokeBegin(e); // assume e catching the correct xy...
44156         }
44157     },
44158     
44159     _handleTouchMove: function (e) {
44160         e.preventDefault();
44161         // var touch = event.targetTouches[0];
44162         // _this._strokeMoveUpdate(touch);
44163         this.strokeMoveUpdate(e);
44164     },
44165     
44166     _handleTouchEnd: function (e) {
44167         var wasCanvasTouched = e.target === this.canvasEl().dom;
44168         if (wasCanvasTouched) {
44169             e.preventDefault();
44170             // var touch = event.changedTouches[0];
44171             // _this._strokeEnd(touch);
44172             this.strokeEnd(e);
44173         }
44174     },
44175     
44176     reset: function () {
44177         this._lastPoints = [];
44178         this._lastVelocity = 0;
44179         this._lastWidth = (this.min_width + this.max_width) / 2;
44180         this.canvasElCtx().fillStyle = this.dot_color;
44181     },
44182     
44183     strokeMoveUpdate: function(e)
44184     {
44185         this.strokeUpdate(e);
44186         
44187         if (this.throttle) {
44188             this.throttleStroke(this.strokeUpdate, this.throttle);
44189         }
44190         else {
44191             this.strokeUpdate(e);
44192         }
44193     },
44194     
44195     strokeBegin: function(e)
44196     {
44197         var newPointGroup = {
44198             color: this.dot_color,
44199             points: []
44200         };
44201         
44202         if (typeof this.onBegin === 'function') {
44203             this.onBegin(e);
44204         }
44205         
44206         this.curve_data.push(newPointGroup);
44207         this.reset();
44208         this.strokeUpdate(e);
44209     },
44210     
44211     strokeUpdate: function(e)
44212     {
44213         var rect = this.canvasEl().dom.getBoundingClientRect();
44214         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44215         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44216         var lastPoints = lastPointGroup.points;
44217         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44218         var isLastPointTooClose = lastPoint
44219             ? point.distanceTo(lastPoint) <= this.min_distance
44220             : false;
44221         var color = lastPointGroup.color;
44222         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44223             var curve = this.addPoint(point);
44224             if (!lastPoint) {
44225                 this.drawDot({color: color, point: point});
44226             }
44227             else if (curve) {
44228                 this.drawCurve({color: color, curve: curve});
44229             }
44230             lastPoints.push({
44231                 time: point.time,
44232                 x: point.x,
44233                 y: point.y
44234             });
44235         }
44236     },
44237     
44238     strokeEnd: function(e)
44239     {
44240         this.strokeUpdate(e);
44241         if (typeof this.onEnd === 'function') {
44242             this.onEnd(e);
44243         }
44244     },
44245     
44246     addPoint:  function (point) {
44247         var _lastPoints = this._lastPoints;
44248         _lastPoints.push(point);
44249         if (_lastPoints.length > 2) {
44250             if (_lastPoints.length === 3) {
44251                 _lastPoints.unshift(_lastPoints[0]);
44252             }
44253             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44254             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44255             _lastPoints.shift();
44256             return curve;
44257         }
44258         return null;
44259     },
44260     
44261     calculateCurveWidths: function (startPoint, endPoint) {
44262         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44263             (1 - this.velocity_filter_weight) * this._lastVelocity;
44264
44265         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44266         var widths = {
44267             end: newWidth,
44268             start: this._lastWidth
44269         };
44270         
44271         this._lastVelocity = velocity;
44272         this._lastWidth = newWidth;
44273         return widths;
44274     },
44275     
44276     drawDot: function (_a) {
44277         var color = _a.color, point = _a.point;
44278         var ctx = this.canvasElCtx();
44279         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44280         ctx.beginPath();
44281         this.drawCurveSegment(point.x, point.y, width);
44282         ctx.closePath();
44283         ctx.fillStyle = color;
44284         ctx.fill();
44285     },
44286     
44287     drawCurve: function (_a) {
44288         var color = _a.color, curve = _a.curve;
44289         var ctx = this.canvasElCtx();
44290         var widthDelta = curve.endWidth - curve.startWidth;
44291         var drawSteps = Math.floor(curve.length()) * 2;
44292         ctx.beginPath();
44293         ctx.fillStyle = color;
44294         for (var i = 0; i < drawSteps; i += 1) {
44295         var t = i / drawSteps;
44296         var tt = t * t;
44297         var ttt = tt * t;
44298         var u = 1 - t;
44299         var uu = u * u;
44300         var uuu = uu * u;
44301         var x = uuu * curve.startPoint.x;
44302         x += 3 * uu * t * curve.control1.x;
44303         x += 3 * u * tt * curve.control2.x;
44304         x += ttt * curve.endPoint.x;
44305         var y = uuu * curve.startPoint.y;
44306         y += 3 * uu * t * curve.control1.y;
44307         y += 3 * u * tt * curve.control2.y;
44308         y += ttt * curve.endPoint.y;
44309         var width = curve.startWidth + ttt * widthDelta;
44310         this.drawCurveSegment(x, y, width);
44311         }
44312         ctx.closePath();
44313         ctx.fill();
44314     },
44315     
44316     drawCurveSegment: function (x, y, width) {
44317         var ctx = this.canvasElCtx();
44318         ctx.moveTo(x, y);
44319         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44320         this.is_empty = false;
44321     },
44322     
44323     clear: function()
44324     {
44325         var ctx = this.canvasElCtx();
44326         var canvas = this.canvasEl().dom;
44327         ctx.fillStyle = this.bg_color;
44328         ctx.clearRect(0, 0, canvas.width, canvas.height);
44329         ctx.fillRect(0, 0, canvas.width, canvas.height);
44330         this.curve_data = [];
44331         this.reset();
44332         this.is_empty = true;
44333     },
44334     
44335     fileEl: function()
44336     {
44337         return  this.el.select('input',true).first();
44338     },
44339     
44340     canvasEl: function()
44341     {
44342         return this.el.select('canvas',true).first();
44343     },
44344     
44345     canvasElCtx: function()
44346     {
44347         return this.el.select('canvas',true).first().dom.getContext('2d');
44348     },
44349     
44350     getImage: function(type)
44351     {
44352         if(this.is_empty) {
44353             return false;
44354         }
44355         
44356         // encryption ?
44357         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44358     },
44359     
44360     drawFromImage: function(img_src)
44361     {
44362         var img = new Image();
44363         
44364         img.onload = function(){
44365             this.canvasElCtx().drawImage(img, 0, 0);
44366         }.bind(this);
44367         
44368         img.src = img_src;
44369         
44370         this.is_empty = false;
44371     },
44372     
44373     selectImage: function()
44374     {
44375         this.fileEl().dom.click();
44376     },
44377     
44378     uploadImage: function(e)
44379     {
44380         var reader = new FileReader();
44381         
44382         reader.onload = function(e){
44383             var img = new Image();
44384             img.onload = function(){
44385                 this.reset();
44386                 this.canvasElCtx().drawImage(img, 0, 0);
44387             }.bind(this);
44388             img.src = e.target.result;
44389         }.bind(this);
44390         
44391         reader.readAsDataURL(e.target.files[0]);
44392     },
44393     
44394     // Bezier Point Constructor
44395     Point: (function () {
44396         function Point(x, y, time) {
44397             this.x = x;
44398             this.y = y;
44399             this.time = time || Date.now();
44400         }
44401         Point.prototype.distanceTo = function (start) {
44402             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44403         };
44404         Point.prototype.equals = function (other) {
44405             return this.x === other.x && this.y === other.y && this.time === other.time;
44406         };
44407         Point.prototype.velocityFrom = function (start) {
44408             return this.time !== start.time
44409             ? this.distanceTo(start) / (this.time - start.time)
44410             : 0;
44411         };
44412         return Point;
44413     }()),
44414     
44415     
44416     // Bezier Constructor
44417     Bezier: (function () {
44418         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44419             this.startPoint = startPoint;
44420             this.control2 = control2;
44421             this.control1 = control1;
44422             this.endPoint = endPoint;
44423             this.startWidth = startWidth;
44424             this.endWidth = endWidth;
44425         }
44426         Bezier.fromPoints = function (points, widths, scope) {
44427             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44428             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44429             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44430         };
44431         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44432             var dx1 = s1.x - s2.x;
44433             var dy1 = s1.y - s2.y;
44434             var dx2 = s2.x - s3.x;
44435             var dy2 = s2.y - s3.y;
44436             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44437             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44438             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44439             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44440             var dxm = m1.x - m2.x;
44441             var dym = m1.y - m2.y;
44442             var k = l2 / (l1 + l2);
44443             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44444             var tx = s2.x - cm.x;
44445             var ty = s2.y - cm.y;
44446             return {
44447                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44448                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44449             };
44450         };
44451         Bezier.prototype.length = function () {
44452             var steps = 10;
44453             var length = 0;
44454             var px;
44455             var py;
44456             for (var i = 0; i <= steps; i += 1) {
44457                 var t = i / steps;
44458                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44459                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44460                 if (i > 0) {
44461                     var xdiff = cx - px;
44462                     var ydiff = cy - py;
44463                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44464                 }
44465                 px = cx;
44466                 py = cy;
44467             }
44468             return length;
44469         };
44470         Bezier.prototype.point = function (t, start, c1, c2, end) {
44471             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44472             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44473             + (3.0 * c2 * (1.0 - t) * t * t)
44474             + (end * t * t * t);
44475         };
44476         return Bezier;
44477     }()),
44478     
44479     throttleStroke: function(fn, wait) {
44480       if (wait === void 0) { wait = 250; }
44481       var previous = 0;
44482       var timeout = null;
44483       var result;
44484       var storedContext;
44485       var storedArgs;
44486       var later = function () {
44487           previous = Date.now();
44488           timeout = null;
44489           result = fn.apply(storedContext, storedArgs);
44490           if (!timeout) {
44491               storedContext = null;
44492               storedArgs = [];
44493           }
44494       };
44495       return function wrapper() {
44496           var args = [];
44497           for (var _i = 0; _i < arguments.length; _i++) {
44498               args[_i] = arguments[_i];
44499           }
44500           var now = Date.now();
44501           var remaining = wait - (now - previous);
44502           storedContext = this;
44503           storedArgs = args;
44504           if (remaining <= 0 || remaining > wait) {
44505               if (timeout) {
44506                   clearTimeout(timeout);
44507                   timeout = null;
44508               }
44509               previous = now;
44510               result = fn.apply(storedContext, storedArgs);
44511               if (!timeout) {
44512                   storedContext = null;
44513                   storedArgs = [];
44514               }
44515           }
44516           else if (!timeout) {
44517               timeout = window.setTimeout(later, remaining);
44518           }
44519           return result;
44520       };
44521   }
44522   
44523 });
44524
44525  
44526
44527