Changed docs/json/roodata.jsondocs/src/Roo_bootstrap_CardUploader.js.htmldocs/symbols...
[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     
12505 };
12506
12507 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12508     
12509      
12510     errorTimeout : 3000,
12511      
12512     images : false,
12513    
12514     fileCollection : false,
12515     allowBlank : true,
12516     
12517     getAutoCreate : function()
12518     {
12519         
12520         var cfg =  {
12521             cls :'form-group' ,
12522             cn : [
12523                
12524                 {
12525                     tag: 'label',
12526                    //cls : 'input-group-addon',
12527                     html : this.fieldLabel
12528
12529                 },
12530
12531                 {
12532                     tag: 'input',
12533                     type : 'hidden',
12534                     name : this.name,
12535                     value : this.value,
12536                     cls : 'd-none  form-control'
12537                 },
12538                 
12539                 {
12540                     tag: 'input',
12541                     multiple : 'multiple',
12542                     type : 'file',
12543                     cls : 'd-none  roo-card-upload-selector'
12544                 },
12545                 
12546                 {
12547                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12548                 },
12549                 {
12550                     cls : 'card-columns roo-card-uploader-container'
12551                 }
12552
12553             ]
12554         };
12555            
12556          
12557         return cfg;
12558     },
12559     
12560     getChildContainer : function() /// what children are added to.
12561     {
12562         return this.containerEl;
12563     },
12564    
12565     getButtonContainer : function() /// what children are added to.
12566     {
12567         return this.el.select(".roo-card-uploader-button-container").first();
12568     },
12569    
12570     initEvents : function()
12571     {
12572         
12573         Roo.bootstrap.Input.prototype.initEvents.call(this);
12574         
12575         var t = this;
12576         this.addxtype({
12577             xns: Roo.bootstrap,
12578
12579             xtype : 'Button',
12580             container_method : 'getButtonContainer' ,            
12581             html :  this.html, // fix changable?
12582             cls : 'w-100 ',
12583             listeners : {
12584                 'click' : function(btn, e) {
12585                     t.onClick(e);
12586                 }
12587             }
12588         });
12589         
12590         
12591         
12592         
12593         this.urlAPI = (window.createObjectURL && window) || 
12594                                 (window.URL && URL.revokeObjectURL && URL) || 
12595                                 (window.webkitURL && webkitURL);
12596                         
12597          
12598          
12599          
12600         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12601         
12602         this.selectorEl.on('change', this.onFileSelected, this);
12603         if (this.images) {
12604             var t = this;
12605             this.images.forEach(function(img) {
12606                 t.addCard(img)
12607             });
12608             this.images = false;
12609         }
12610         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12611          
12612        
12613     },
12614     
12615    
12616     onClick : function(e)
12617     {
12618         e.preventDefault();
12619          
12620         this.selectorEl.dom.click();
12621          
12622     },
12623     
12624     onFileSelected : function(e)
12625     {
12626         e.preventDefault();
12627         
12628         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12629             return;
12630         }
12631         
12632         Roo.each(this.selectorEl.dom.files, function(file){    
12633             this.addFile(file);
12634         }, this);
12635          
12636     },
12637     
12638       
12639     
12640       
12641     
12642     addFile : function(file)
12643     {
12644            
12645         if(typeof(file) === 'string'){
12646             throw "Add file by name?"; // should not happen
12647             return;
12648         }
12649         
12650         if(!file || !this.urlAPI){
12651             return;
12652         }
12653         
12654         // file;
12655         // file.type;
12656         
12657         var _this = this;
12658         
12659         
12660         var url = _this.urlAPI.createObjectURL( file);
12661            
12662         this.addCard({
12663             id : Roo.bootstrap.CardUploader.ID--,
12664             is_uploaded : false,
12665             src : url,
12666             srcfile : file,
12667             title : file.name,
12668             mimetype : file.type,
12669             preview : false,
12670             is_deleted : 0
12671         });
12672         
12673     },
12674     
12675     addCard : function (data)
12676     {
12677         // hidden input element?
12678         // if the file is not an image...
12679         //then we need to use something other that and header_image
12680         var t = this;
12681         //   remove.....
12682         var footer = [
12683             {
12684                 xns : Roo.bootstrap,
12685                 xtype : 'CardFooter',
12686                  items: [
12687                     {
12688                         xns : Roo.bootstrap,
12689                         xtype : 'Element',
12690                         cls : 'd-flex',
12691                         items : [
12692                             
12693                             {
12694                                 xns : Roo.bootstrap,
12695                                 xtype : 'Button',
12696                                 html : String.format("<small>{0}</small>", data.title),
12697                                 cls : 'col-10 text-left',
12698                                 size: 'sm',
12699                                 weight: 'link',
12700                                 fa : 'download',
12701                                 listeners : {
12702                                     click : function() {
12703                                         this.downloadCard(data.id)
12704                                     }
12705                                 }
12706                             },
12707                           
12708                             {
12709                                 xns : Roo.bootstrap,
12710                                 xtype : 'Button',
12711                                 style: 'max-height: 28px; ',
12712                                 size : 'sm',
12713                                 weight: 'danger',
12714                                 cls : 'col-2',
12715                                 fa : 'times',
12716                                 listeners : {
12717                                     click : function() {
12718                                         t.removeCard(data.id)
12719                                     }
12720                                 }
12721                             }
12722                         ]
12723                     }
12724                     
12725                 ] 
12726             }
12727             
12728         ];
12729         
12730         var cn = this.addxtype(
12731             {
12732                  
12733                 xns : Roo.bootstrap,
12734                 xtype : 'Card',
12735                 closeable : true,
12736                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12737                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12738                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12739                 data : data,
12740                 html : false,
12741                  
12742                 items : footer,
12743                 initEvents : function() {
12744                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12745                     this.imgEl = this.el.select('.card-img-top').first();
12746                     if (this.imgEl) {
12747                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12748                         this.imgEl.set({ 'pointer' : 'cursor' });
12749                                   
12750                     }
12751                     this.getCardFooter().addClass('p-1');
12752                     
12753                   
12754                 }
12755                 
12756             }
12757         );
12758         // dont' really need ot update items.
12759         // this.items.push(cn);
12760         this.fileCollection.add(cn);
12761         
12762         if (!data.srcfile) {
12763             this.updateInput();
12764             return;
12765         }
12766             
12767         var _t = this;
12768         var reader = new FileReader();
12769         reader.addEventListener("load", function() {  
12770             data.srcdata =  reader.result;
12771             _t.updateInput();
12772         });
12773         reader.readAsDataURL(data.srcfile);
12774         
12775         
12776         
12777     },
12778     removeCard : function(id)
12779     {
12780         
12781         var card  = this.fileCollection.get(id);
12782         card.data.is_deleted = 1;
12783         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12784         //this.fileCollection.remove(card);
12785         //this.items = this.items.filter(function(e) { return e != card });
12786         // dont' really need ot update items.
12787         card.el.dom.parentNode.removeChild(card.el.dom);
12788         this.updateInput();
12789
12790         
12791     },
12792     reset: function()
12793     {
12794         this.fileCollection.each(function(card) {
12795             if (card.el.dom && card.el.dom.parentNode) {
12796                 card.el.dom.parentNode.removeChild(card.el.dom);
12797             }
12798         });
12799         this.fileCollection.clear();
12800         this.updateInput();
12801     },
12802     
12803     updateInput : function()
12804     {
12805          var data = [];
12806         this.fileCollection.each(function(e) {
12807             data.push(e.data);
12808             
12809         });
12810         this.inputEl().dom.value = JSON.stringify(data);
12811         
12812         
12813         
12814     }
12815     
12816     
12817 });
12818
12819
12820 Roo.bootstrap.CardUploader.ID = -1;/*
12821  * Based on:
12822  * Ext JS Library 1.1.1
12823  * Copyright(c) 2006-2007, Ext JS, LLC.
12824  *
12825  * Originally Released Under LGPL - original licence link has changed is not relivant.
12826  *
12827  * Fork - LGPL
12828  * <script type="text/javascript">
12829  */
12830
12831
12832 /**
12833  * @class Roo.data.SortTypes
12834  * @singleton
12835  * Defines the default sorting (casting?) comparison functions used when sorting data.
12836  */
12837 Roo.data.SortTypes = {
12838     /**
12839      * Default sort that does nothing
12840      * @param {Mixed} s The value being converted
12841      * @return {Mixed} The comparison value
12842      */
12843     none : function(s){
12844         return s;
12845     },
12846     
12847     /**
12848      * The regular expression used to strip tags
12849      * @type {RegExp}
12850      * @property
12851      */
12852     stripTagsRE : /<\/?[^>]+>/gi,
12853     
12854     /**
12855      * Strips all HTML tags to sort on text only
12856      * @param {Mixed} s The value being converted
12857      * @return {String} The comparison value
12858      */
12859     asText : function(s){
12860         return String(s).replace(this.stripTagsRE, "");
12861     },
12862     
12863     /**
12864      * Strips all HTML tags to sort on text only - Case insensitive
12865      * @param {Mixed} s The value being converted
12866      * @return {String} The comparison value
12867      */
12868     asUCText : function(s){
12869         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12870     },
12871     
12872     /**
12873      * Case insensitive string
12874      * @param {Mixed} s The value being converted
12875      * @return {String} The comparison value
12876      */
12877     asUCString : function(s) {
12878         return String(s).toUpperCase();
12879     },
12880     
12881     /**
12882      * Date sorting
12883      * @param {Mixed} s The value being converted
12884      * @return {Number} The comparison value
12885      */
12886     asDate : function(s) {
12887         if(!s){
12888             return 0;
12889         }
12890         if(s instanceof Date){
12891             return s.getTime();
12892         }
12893         return Date.parse(String(s));
12894     },
12895     
12896     /**
12897      * Float sorting
12898      * @param {Mixed} s The value being converted
12899      * @return {Float} The comparison value
12900      */
12901     asFloat : function(s) {
12902         var val = parseFloat(String(s).replace(/,/g, ""));
12903         if(isNaN(val)) {
12904             val = 0;
12905         }
12906         return val;
12907     },
12908     
12909     /**
12910      * Integer sorting
12911      * @param {Mixed} s The value being converted
12912      * @return {Number} The comparison value
12913      */
12914     asInt : function(s) {
12915         var val = parseInt(String(s).replace(/,/g, ""));
12916         if(isNaN(val)) {
12917             val = 0;
12918         }
12919         return val;
12920     }
12921 };/*
12922  * Based on:
12923  * Ext JS Library 1.1.1
12924  * Copyright(c) 2006-2007, Ext JS, LLC.
12925  *
12926  * Originally Released Under LGPL - original licence link has changed is not relivant.
12927  *
12928  * Fork - LGPL
12929  * <script type="text/javascript">
12930  */
12931
12932 /**
12933 * @class Roo.data.Record
12934  * Instances of this class encapsulate both record <em>definition</em> information, and record
12935  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12936  * to access Records cached in an {@link Roo.data.Store} object.<br>
12937  * <p>
12938  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12939  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12940  * objects.<br>
12941  * <p>
12942  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12943  * @constructor
12944  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12945  * {@link #create}. The parameters are the same.
12946  * @param {Array} data An associative Array of data values keyed by the field name.
12947  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12948  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12949  * not specified an integer id is generated.
12950  */
12951 Roo.data.Record = function(data, id){
12952     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12953     this.data = data;
12954 };
12955
12956 /**
12957  * Generate a constructor for a specific record layout.
12958  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12959  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12960  * Each field definition object may contain the following properties: <ul>
12961  * <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,
12962  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12963  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12964  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12965  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12966  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12967  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12968  * this may be omitted.</p></li>
12969  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12970  * <ul><li>auto (Default, implies no conversion)</li>
12971  * <li>string</li>
12972  * <li>int</li>
12973  * <li>float</li>
12974  * <li>boolean</li>
12975  * <li>date</li></ul></p></li>
12976  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12977  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12978  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12979  * by the Reader into an object that will be stored in the Record. It is passed the
12980  * following parameters:<ul>
12981  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12982  * </ul></p></li>
12983  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12984  * </ul>
12985  * <br>usage:<br><pre><code>
12986 var TopicRecord = Roo.data.Record.create(
12987     {name: 'title', mapping: 'topic_title'},
12988     {name: 'author', mapping: 'username'},
12989     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12990     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12991     {name: 'lastPoster', mapping: 'user2'},
12992     {name: 'excerpt', mapping: 'post_text'}
12993 );
12994
12995 var myNewRecord = new TopicRecord({
12996     title: 'Do my job please',
12997     author: 'noobie',
12998     totalPosts: 1,
12999     lastPost: new Date(),
13000     lastPoster: 'Animal',
13001     excerpt: 'No way dude!'
13002 });
13003 myStore.add(myNewRecord);
13004 </code></pre>
13005  * @method create
13006  * @static
13007  */
13008 Roo.data.Record.create = function(o){
13009     var f = function(){
13010         f.superclass.constructor.apply(this, arguments);
13011     };
13012     Roo.extend(f, Roo.data.Record);
13013     var p = f.prototype;
13014     p.fields = new Roo.util.MixedCollection(false, function(field){
13015         return field.name;
13016     });
13017     for(var i = 0, len = o.length; i < len; i++){
13018         p.fields.add(new Roo.data.Field(o[i]));
13019     }
13020     f.getField = function(name){
13021         return p.fields.get(name);  
13022     };
13023     return f;
13024 };
13025
13026 Roo.data.Record.AUTO_ID = 1000;
13027 Roo.data.Record.EDIT = 'edit';
13028 Roo.data.Record.REJECT = 'reject';
13029 Roo.data.Record.COMMIT = 'commit';
13030
13031 Roo.data.Record.prototype = {
13032     /**
13033      * Readonly flag - true if this record has been modified.
13034      * @type Boolean
13035      */
13036     dirty : false,
13037     editing : false,
13038     error: null,
13039     modified: null,
13040
13041     // private
13042     join : function(store){
13043         this.store = store;
13044     },
13045
13046     /**
13047      * Set the named field to the specified value.
13048      * @param {String} name The name of the field to set.
13049      * @param {Object} value The value to set the field to.
13050      */
13051     set : function(name, value){
13052         if(this.data[name] == value){
13053             return;
13054         }
13055         this.dirty = true;
13056         if(!this.modified){
13057             this.modified = {};
13058         }
13059         if(typeof this.modified[name] == 'undefined'){
13060             this.modified[name] = this.data[name];
13061         }
13062         this.data[name] = value;
13063         if(!this.editing && this.store){
13064             this.store.afterEdit(this);
13065         }       
13066     },
13067
13068     /**
13069      * Get the value of the named field.
13070      * @param {String} name The name of the field to get the value of.
13071      * @return {Object} The value of the field.
13072      */
13073     get : function(name){
13074         return this.data[name]; 
13075     },
13076
13077     // private
13078     beginEdit : function(){
13079         this.editing = true;
13080         this.modified = {}; 
13081     },
13082
13083     // private
13084     cancelEdit : function(){
13085         this.editing = false;
13086         delete this.modified;
13087     },
13088
13089     // private
13090     endEdit : function(){
13091         this.editing = false;
13092         if(this.dirty && this.store){
13093             this.store.afterEdit(this);
13094         }
13095     },
13096
13097     /**
13098      * Usually called by the {@link Roo.data.Store} which owns the Record.
13099      * Rejects all changes made to the Record since either creation, or the last commit operation.
13100      * Modified fields are reverted to their original values.
13101      * <p>
13102      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13103      * of reject operations.
13104      */
13105     reject : function(){
13106         var m = this.modified;
13107         for(var n in m){
13108             if(typeof m[n] != "function"){
13109                 this.data[n] = m[n];
13110             }
13111         }
13112         this.dirty = false;
13113         delete this.modified;
13114         this.editing = false;
13115         if(this.store){
13116             this.store.afterReject(this);
13117         }
13118     },
13119
13120     /**
13121      * Usually called by the {@link Roo.data.Store} which owns the Record.
13122      * Commits all changes made to the Record since either creation, or the last commit operation.
13123      * <p>
13124      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13125      * of commit operations.
13126      */
13127     commit : function(){
13128         this.dirty = false;
13129         delete this.modified;
13130         this.editing = false;
13131         if(this.store){
13132             this.store.afterCommit(this);
13133         }
13134     },
13135
13136     // private
13137     hasError : function(){
13138         return this.error != null;
13139     },
13140
13141     // private
13142     clearError : function(){
13143         this.error = null;
13144     },
13145
13146     /**
13147      * Creates a copy of this record.
13148      * @param {String} id (optional) A new record id if you don't want to use this record's id
13149      * @return {Record}
13150      */
13151     copy : function(newId) {
13152         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13153     }
13154 };/*
13155  * Based on:
13156  * Ext JS Library 1.1.1
13157  * Copyright(c) 2006-2007, Ext JS, LLC.
13158  *
13159  * Originally Released Under LGPL - original licence link has changed is not relivant.
13160  *
13161  * Fork - LGPL
13162  * <script type="text/javascript">
13163  */
13164
13165
13166
13167 /**
13168  * @class Roo.data.Store
13169  * @extends Roo.util.Observable
13170  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13171  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13172  * <p>
13173  * 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
13174  * has no knowledge of the format of the data returned by the Proxy.<br>
13175  * <p>
13176  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13177  * instances from the data object. These records are cached and made available through accessor functions.
13178  * @constructor
13179  * Creates a new Store.
13180  * @param {Object} config A config object containing the objects needed for the Store to access data,
13181  * and read the data into Records.
13182  */
13183 Roo.data.Store = function(config){
13184     this.data = new Roo.util.MixedCollection(false);
13185     this.data.getKey = function(o){
13186         return o.id;
13187     };
13188     this.baseParams = {};
13189     // private
13190     this.paramNames = {
13191         "start" : "start",
13192         "limit" : "limit",
13193         "sort" : "sort",
13194         "dir" : "dir",
13195         "multisort" : "_multisort"
13196     };
13197
13198     if(config && config.data){
13199         this.inlineData = config.data;
13200         delete config.data;
13201     }
13202
13203     Roo.apply(this, config);
13204     
13205     if(this.reader){ // reader passed
13206         this.reader = Roo.factory(this.reader, Roo.data);
13207         this.reader.xmodule = this.xmodule || false;
13208         if(!this.recordType){
13209             this.recordType = this.reader.recordType;
13210         }
13211         if(this.reader.onMetaChange){
13212             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13213         }
13214     }
13215
13216     if(this.recordType){
13217         this.fields = this.recordType.prototype.fields;
13218     }
13219     this.modified = [];
13220
13221     this.addEvents({
13222         /**
13223          * @event datachanged
13224          * Fires when the data cache has changed, and a widget which is using this Store
13225          * as a Record cache should refresh its view.
13226          * @param {Store} this
13227          */
13228         datachanged : true,
13229         /**
13230          * @event metachange
13231          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13232          * @param {Store} this
13233          * @param {Object} meta The JSON metadata
13234          */
13235         metachange : true,
13236         /**
13237          * @event add
13238          * Fires when Records have been added to the Store
13239          * @param {Store} this
13240          * @param {Roo.data.Record[]} records The array of Records added
13241          * @param {Number} index The index at which the record(s) were added
13242          */
13243         add : true,
13244         /**
13245          * @event remove
13246          * Fires when a Record has been removed from the Store
13247          * @param {Store} this
13248          * @param {Roo.data.Record} record The Record that was removed
13249          * @param {Number} index The index at which the record was removed
13250          */
13251         remove : true,
13252         /**
13253          * @event update
13254          * Fires when a Record has been updated
13255          * @param {Store} this
13256          * @param {Roo.data.Record} record The Record that was updated
13257          * @param {String} operation The update operation being performed.  Value may be one of:
13258          * <pre><code>
13259  Roo.data.Record.EDIT
13260  Roo.data.Record.REJECT
13261  Roo.data.Record.COMMIT
13262          * </code></pre>
13263          */
13264         update : true,
13265         /**
13266          * @event clear
13267          * Fires when the data cache has been cleared.
13268          * @param {Store} this
13269          */
13270         clear : true,
13271         /**
13272          * @event beforeload
13273          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13274          * the load action will be canceled.
13275          * @param {Store} this
13276          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13277          */
13278         beforeload : true,
13279         /**
13280          * @event beforeloadadd
13281          * Fires after a new set of Records has been loaded.
13282          * @param {Store} this
13283          * @param {Roo.data.Record[]} records The Records that were loaded
13284          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13285          */
13286         beforeloadadd : true,
13287         /**
13288          * @event load
13289          * Fires after a new set of Records has been loaded, before they are added to the store.
13290          * @param {Store} this
13291          * @param {Roo.data.Record[]} records The Records that were loaded
13292          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13293          * @params {Object} return from reader
13294          */
13295         load : true,
13296         /**
13297          * @event loadexception
13298          * Fires if an exception occurs in the Proxy during loading.
13299          * Called with the signature of the Proxy's "loadexception" event.
13300          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13301          * 
13302          * @param {Proxy} 
13303          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13304          * @param {Object} load options 
13305          * @param {Object} jsonData from your request (normally this contains the Exception)
13306          */
13307         loadexception : true
13308     });
13309     
13310     if(this.proxy){
13311         this.proxy = Roo.factory(this.proxy, Roo.data);
13312         this.proxy.xmodule = this.xmodule || false;
13313         this.relayEvents(this.proxy,  ["loadexception"]);
13314     }
13315     this.sortToggle = {};
13316     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13317
13318     Roo.data.Store.superclass.constructor.call(this);
13319
13320     if(this.inlineData){
13321         this.loadData(this.inlineData);
13322         delete this.inlineData;
13323     }
13324 };
13325
13326 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13327      /**
13328     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13329     * without a remote query - used by combo/forms at present.
13330     */
13331     
13332     /**
13333     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13334     */
13335     /**
13336     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13337     */
13338     /**
13339     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13340     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13341     */
13342     /**
13343     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13344     * on any HTTP request
13345     */
13346     /**
13347     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13348     */
13349     /**
13350     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13351     */
13352     multiSort: false,
13353     /**
13354     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13355     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13356     */
13357     remoteSort : false,
13358
13359     /**
13360     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13361      * loaded or when a record is removed. (defaults to false).
13362     */
13363     pruneModifiedRecords : false,
13364
13365     // private
13366     lastOptions : null,
13367
13368     /**
13369      * Add Records to the Store and fires the add event.
13370      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13371      */
13372     add : function(records){
13373         records = [].concat(records);
13374         for(var i = 0, len = records.length; i < len; i++){
13375             records[i].join(this);
13376         }
13377         var index = this.data.length;
13378         this.data.addAll(records);
13379         this.fireEvent("add", this, records, index);
13380     },
13381
13382     /**
13383      * Remove a Record from the Store and fires the remove event.
13384      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13385      */
13386     remove : function(record){
13387         var index = this.data.indexOf(record);
13388         this.data.removeAt(index);
13389  
13390         if(this.pruneModifiedRecords){
13391             this.modified.remove(record);
13392         }
13393         this.fireEvent("remove", this, record, index);
13394     },
13395
13396     /**
13397      * Remove all Records from the Store and fires the clear event.
13398      */
13399     removeAll : function(){
13400         this.data.clear();
13401         if(this.pruneModifiedRecords){
13402             this.modified = [];
13403         }
13404         this.fireEvent("clear", this);
13405     },
13406
13407     /**
13408      * Inserts Records to the Store at the given index and fires the add event.
13409      * @param {Number} index The start index at which to insert the passed Records.
13410      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13411      */
13412     insert : function(index, records){
13413         records = [].concat(records);
13414         for(var i = 0, len = records.length; i < len; i++){
13415             this.data.insert(index, records[i]);
13416             records[i].join(this);
13417         }
13418         this.fireEvent("add", this, records, index);
13419     },
13420
13421     /**
13422      * Get the index within the cache of the passed Record.
13423      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13424      * @return {Number} The index of the passed Record. Returns -1 if not found.
13425      */
13426     indexOf : function(record){
13427         return this.data.indexOf(record);
13428     },
13429
13430     /**
13431      * Get the index within the cache of the Record with the passed id.
13432      * @param {String} id The id of the Record to find.
13433      * @return {Number} The index of the Record. Returns -1 if not found.
13434      */
13435     indexOfId : function(id){
13436         return this.data.indexOfKey(id);
13437     },
13438
13439     /**
13440      * Get the Record with the specified id.
13441      * @param {String} id The id of the Record to find.
13442      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13443      */
13444     getById : function(id){
13445         return this.data.key(id);
13446     },
13447
13448     /**
13449      * Get the Record at the specified index.
13450      * @param {Number} index The index of the Record to find.
13451      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13452      */
13453     getAt : function(index){
13454         return this.data.itemAt(index);
13455     },
13456
13457     /**
13458      * Returns a range of Records between specified indices.
13459      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13460      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13461      * @return {Roo.data.Record[]} An array of Records
13462      */
13463     getRange : function(start, end){
13464         return this.data.getRange(start, end);
13465     },
13466
13467     // private
13468     storeOptions : function(o){
13469         o = Roo.apply({}, o);
13470         delete o.callback;
13471         delete o.scope;
13472         this.lastOptions = o;
13473     },
13474
13475     /**
13476      * Loads the Record cache from the configured Proxy using the configured Reader.
13477      * <p>
13478      * If using remote paging, then the first load call must specify the <em>start</em>
13479      * and <em>limit</em> properties in the options.params property to establish the initial
13480      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13481      * <p>
13482      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13483      * and this call will return before the new data has been loaded. Perform any post-processing
13484      * in a callback function, or in a "load" event handler.</strong>
13485      * <p>
13486      * @param {Object} options An object containing properties which control loading options:<ul>
13487      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13488      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13489      * passed the following arguments:<ul>
13490      * <li>r : Roo.data.Record[]</li>
13491      * <li>options: Options object from the load call</li>
13492      * <li>success: Boolean success indicator</li></ul></li>
13493      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13494      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13495      * </ul>
13496      */
13497     load : function(options){
13498         options = options || {};
13499         if(this.fireEvent("beforeload", this, options) !== false){
13500             this.storeOptions(options);
13501             var p = Roo.apply(options.params || {}, this.baseParams);
13502             // if meta was not loaded from remote source.. try requesting it.
13503             if (!this.reader.metaFromRemote) {
13504                 p._requestMeta = 1;
13505             }
13506             if(this.sortInfo && this.remoteSort){
13507                 var pn = this.paramNames;
13508                 p[pn["sort"]] = this.sortInfo.field;
13509                 p[pn["dir"]] = this.sortInfo.direction;
13510             }
13511             if (this.multiSort) {
13512                 var pn = this.paramNames;
13513                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13514             }
13515             
13516             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13517         }
13518     },
13519
13520     /**
13521      * Reloads the Record cache from the configured Proxy using the configured Reader and
13522      * the options from the last load operation performed.
13523      * @param {Object} options (optional) An object containing properties which may override the options
13524      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13525      * the most recently used options are reused).
13526      */
13527     reload : function(options){
13528         this.load(Roo.applyIf(options||{}, this.lastOptions));
13529     },
13530
13531     // private
13532     // Called as a callback by the Reader during a load operation.
13533     loadRecords : function(o, options, success){
13534         if(!o || success === false){
13535             if(success !== false){
13536                 this.fireEvent("load", this, [], options, o);
13537             }
13538             if(options.callback){
13539                 options.callback.call(options.scope || this, [], options, false);
13540             }
13541             return;
13542         }
13543         // if data returned failure - throw an exception.
13544         if (o.success === false) {
13545             // show a message if no listener is registered.
13546             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13547                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13548             }
13549             // loadmask wil be hooked into this..
13550             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13551             return;
13552         }
13553         var r = o.records, t = o.totalRecords || r.length;
13554         
13555         this.fireEvent("beforeloadadd", this, r, options, o);
13556         
13557         if(!options || options.add !== true){
13558             if(this.pruneModifiedRecords){
13559                 this.modified = [];
13560             }
13561             for(var i = 0, len = r.length; i < len; i++){
13562                 r[i].join(this);
13563             }
13564             if(this.snapshot){
13565                 this.data = this.snapshot;
13566                 delete this.snapshot;
13567             }
13568             this.data.clear();
13569             this.data.addAll(r);
13570             this.totalLength = t;
13571             this.applySort();
13572             this.fireEvent("datachanged", this);
13573         }else{
13574             this.totalLength = Math.max(t, this.data.length+r.length);
13575             this.add(r);
13576         }
13577         
13578         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13579                 
13580             var e = new Roo.data.Record({});
13581
13582             e.set(this.parent.displayField, this.parent.emptyTitle);
13583             e.set(this.parent.valueField, '');
13584
13585             this.insert(0, e);
13586         }
13587             
13588         this.fireEvent("load", this, r, options, o);
13589         if(options.callback){
13590             options.callback.call(options.scope || this, r, options, true);
13591         }
13592     },
13593
13594
13595     /**
13596      * Loads data from a passed data block. A Reader which understands the format of the data
13597      * must have been configured in the constructor.
13598      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13599      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13600      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13601      */
13602     loadData : function(o, append){
13603         var r = this.reader.readRecords(o);
13604         this.loadRecords(r, {add: append}, true);
13605     },
13606     
13607      /**
13608      * using 'cn' the nested child reader read the child array into it's child stores.
13609      * @param {Object} rec The record with a 'children array
13610      */
13611     loadDataFromChildren : function(rec)
13612     {
13613         this.loadData(this.reader.toLoadData(rec));
13614     },
13615     
13616
13617     /**
13618      * Gets the number of cached records.
13619      * <p>
13620      * <em>If using paging, this may not be the total size of the dataset. If the data object
13621      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13622      * the data set size</em>
13623      */
13624     getCount : function(){
13625         return this.data.length || 0;
13626     },
13627
13628     /**
13629      * Gets the total number of records in the dataset as returned by the server.
13630      * <p>
13631      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13632      * the dataset size</em>
13633      */
13634     getTotalCount : function(){
13635         return this.totalLength || 0;
13636     },
13637
13638     /**
13639      * Returns the sort state of the Store as an object with two properties:
13640      * <pre><code>
13641  field {String} The name of the field by which the Records are sorted
13642  direction {String} The sort order, "ASC" or "DESC"
13643      * </code></pre>
13644      */
13645     getSortState : function(){
13646         return this.sortInfo;
13647     },
13648
13649     // private
13650     applySort : function(){
13651         if(this.sortInfo && !this.remoteSort){
13652             var s = this.sortInfo, f = s.field;
13653             var st = this.fields.get(f).sortType;
13654             var fn = function(r1, r2){
13655                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13656                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13657             };
13658             this.data.sort(s.direction, fn);
13659             if(this.snapshot && this.snapshot != this.data){
13660                 this.snapshot.sort(s.direction, fn);
13661             }
13662         }
13663     },
13664
13665     /**
13666      * Sets the default sort column and order to be used by the next load operation.
13667      * @param {String} fieldName The name of the field to sort by.
13668      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13669      */
13670     setDefaultSort : function(field, dir){
13671         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13672     },
13673
13674     /**
13675      * Sort the Records.
13676      * If remote sorting is used, the sort is performed on the server, and the cache is
13677      * reloaded. If local sorting is used, the cache is sorted internally.
13678      * @param {String} fieldName The name of the field to sort by.
13679      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13680      */
13681     sort : function(fieldName, dir){
13682         var f = this.fields.get(fieldName);
13683         if(!dir){
13684             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13685             
13686             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13687                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13688             }else{
13689                 dir = f.sortDir;
13690             }
13691         }
13692         this.sortToggle[f.name] = dir;
13693         this.sortInfo = {field: f.name, direction: dir};
13694         if(!this.remoteSort){
13695             this.applySort();
13696             this.fireEvent("datachanged", this);
13697         }else{
13698             this.load(this.lastOptions);
13699         }
13700     },
13701
13702     /**
13703      * Calls the specified function for each of the Records in the cache.
13704      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13705      * Returning <em>false</em> aborts and exits the iteration.
13706      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13707      */
13708     each : function(fn, scope){
13709         this.data.each(fn, scope);
13710     },
13711
13712     /**
13713      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13714      * (e.g., during paging).
13715      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13716      */
13717     getModifiedRecords : function(){
13718         return this.modified;
13719     },
13720
13721     // private
13722     createFilterFn : function(property, value, anyMatch){
13723         if(!value.exec){ // not a regex
13724             value = String(value);
13725             if(value.length == 0){
13726                 return false;
13727             }
13728             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13729         }
13730         return function(r){
13731             return value.test(r.data[property]);
13732         };
13733     },
13734
13735     /**
13736      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13737      * @param {String} property A field on your records
13738      * @param {Number} start The record index to start at (defaults to 0)
13739      * @param {Number} end The last record index to include (defaults to length - 1)
13740      * @return {Number} The sum
13741      */
13742     sum : function(property, start, end){
13743         var rs = this.data.items, v = 0;
13744         start = start || 0;
13745         end = (end || end === 0) ? end : rs.length-1;
13746
13747         for(var i = start; i <= end; i++){
13748             v += (rs[i].data[property] || 0);
13749         }
13750         return v;
13751     },
13752
13753     /**
13754      * Filter the records by a specified property.
13755      * @param {String} field A field on your records
13756      * @param {String/RegExp} value Either a string that the field
13757      * should start with or a RegExp to test against the field
13758      * @param {Boolean} anyMatch True to match any part not just the beginning
13759      */
13760     filter : function(property, value, anyMatch){
13761         var fn = this.createFilterFn(property, value, anyMatch);
13762         return fn ? this.filterBy(fn) : this.clearFilter();
13763     },
13764
13765     /**
13766      * Filter by a function. The specified function will be called with each
13767      * record in this data source. If the function returns true the record is included,
13768      * otherwise it is filtered.
13769      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13770      * @param {Object} scope (optional) The scope of the function (defaults to this)
13771      */
13772     filterBy : function(fn, scope){
13773         this.snapshot = this.snapshot || this.data;
13774         this.data = this.queryBy(fn, scope||this);
13775         this.fireEvent("datachanged", this);
13776     },
13777
13778     /**
13779      * Query the records by a specified property.
13780      * @param {String} field A field on your records
13781      * @param {String/RegExp} value Either a string that the field
13782      * should start with or a RegExp to test against the field
13783      * @param {Boolean} anyMatch True to match any part not just the beginning
13784      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13785      */
13786     query : function(property, value, anyMatch){
13787         var fn = this.createFilterFn(property, value, anyMatch);
13788         return fn ? this.queryBy(fn) : this.data.clone();
13789     },
13790
13791     /**
13792      * Query by a function. The specified function will be called with each
13793      * record in this data source. If the function returns true the record is included
13794      * in the results.
13795      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13796      * @param {Object} scope (optional) The scope of the function (defaults to this)
13797       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13798      **/
13799     queryBy : function(fn, scope){
13800         var data = this.snapshot || this.data;
13801         return data.filterBy(fn, scope||this);
13802     },
13803
13804     /**
13805      * Collects unique values for a particular dataIndex from this store.
13806      * @param {String} dataIndex The property to collect
13807      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13808      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13809      * @return {Array} An array of the unique values
13810      **/
13811     collect : function(dataIndex, allowNull, bypassFilter){
13812         var d = (bypassFilter === true && this.snapshot) ?
13813                 this.snapshot.items : this.data.items;
13814         var v, sv, r = [], l = {};
13815         for(var i = 0, len = d.length; i < len; i++){
13816             v = d[i].data[dataIndex];
13817             sv = String(v);
13818             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13819                 l[sv] = true;
13820                 r[r.length] = v;
13821             }
13822         }
13823         return r;
13824     },
13825
13826     /**
13827      * Revert to a view of the Record cache with no filtering applied.
13828      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13829      */
13830     clearFilter : function(suppressEvent){
13831         if(this.snapshot && this.snapshot != this.data){
13832             this.data = this.snapshot;
13833             delete this.snapshot;
13834             if(suppressEvent !== true){
13835                 this.fireEvent("datachanged", this);
13836             }
13837         }
13838     },
13839
13840     // private
13841     afterEdit : function(record){
13842         if(this.modified.indexOf(record) == -1){
13843             this.modified.push(record);
13844         }
13845         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13846     },
13847     
13848     // private
13849     afterReject : function(record){
13850         this.modified.remove(record);
13851         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13852     },
13853
13854     // private
13855     afterCommit : function(record){
13856         this.modified.remove(record);
13857         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13858     },
13859
13860     /**
13861      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13862      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13863      */
13864     commitChanges : function(){
13865         var m = this.modified.slice(0);
13866         this.modified = [];
13867         for(var i = 0, len = m.length; i < len; i++){
13868             m[i].commit();
13869         }
13870     },
13871
13872     /**
13873      * Cancel outstanding changes on all changed records.
13874      */
13875     rejectChanges : function(){
13876         var m = this.modified.slice(0);
13877         this.modified = [];
13878         for(var i = 0, len = m.length; i < len; i++){
13879             m[i].reject();
13880         }
13881     },
13882
13883     onMetaChange : function(meta, rtype, o){
13884         this.recordType = rtype;
13885         this.fields = rtype.prototype.fields;
13886         delete this.snapshot;
13887         this.sortInfo = meta.sortInfo || this.sortInfo;
13888         this.modified = [];
13889         this.fireEvent('metachange', this, this.reader.meta);
13890     },
13891     
13892     moveIndex : function(data, type)
13893     {
13894         var index = this.indexOf(data);
13895         
13896         var newIndex = index + type;
13897         
13898         this.remove(data);
13899         
13900         this.insert(newIndex, data);
13901         
13902     }
13903 });/*
13904  * Based on:
13905  * Ext JS Library 1.1.1
13906  * Copyright(c) 2006-2007, Ext JS, LLC.
13907  *
13908  * Originally Released Under LGPL - original licence link has changed is not relivant.
13909  *
13910  * Fork - LGPL
13911  * <script type="text/javascript">
13912  */
13913
13914 /**
13915  * @class Roo.data.SimpleStore
13916  * @extends Roo.data.Store
13917  * Small helper class to make creating Stores from Array data easier.
13918  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13919  * @cfg {Array} fields An array of field definition objects, or field name strings.
13920  * @cfg {Object} an existing reader (eg. copied from another store)
13921  * @cfg {Array} data The multi-dimensional array of data
13922  * @constructor
13923  * @param {Object} config
13924  */
13925 Roo.data.SimpleStore = function(config)
13926 {
13927     Roo.data.SimpleStore.superclass.constructor.call(this, {
13928         isLocal : true,
13929         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13930                 id: config.id
13931             },
13932             Roo.data.Record.create(config.fields)
13933         ),
13934         proxy : new Roo.data.MemoryProxy(config.data)
13935     });
13936     this.load();
13937 };
13938 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13939  * Based on:
13940  * Ext JS Library 1.1.1
13941  * Copyright(c) 2006-2007, Ext JS, LLC.
13942  *
13943  * Originally Released Under LGPL - original licence link has changed is not relivant.
13944  *
13945  * Fork - LGPL
13946  * <script type="text/javascript">
13947  */
13948
13949 /**
13950 /**
13951  * @extends Roo.data.Store
13952  * @class Roo.data.JsonStore
13953  * Small helper class to make creating Stores for JSON data easier. <br/>
13954 <pre><code>
13955 var store = new Roo.data.JsonStore({
13956     url: 'get-images.php',
13957     root: 'images',
13958     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13959 });
13960 </code></pre>
13961  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13962  * JsonReader and HttpProxy (unless inline data is provided).</b>
13963  * @cfg {Array} fields An array of field definition objects, or field name strings.
13964  * @constructor
13965  * @param {Object} config
13966  */
13967 Roo.data.JsonStore = function(c){
13968     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13969         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13970         reader: new Roo.data.JsonReader(c, c.fields)
13971     }));
13972 };
13973 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13974  * Based on:
13975  * Ext JS Library 1.1.1
13976  * Copyright(c) 2006-2007, Ext JS, LLC.
13977  *
13978  * Originally Released Under LGPL - original licence link has changed is not relivant.
13979  *
13980  * Fork - LGPL
13981  * <script type="text/javascript">
13982  */
13983
13984  
13985 Roo.data.Field = function(config){
13986     if(typeof config == "string"){
13987         config = {name: config};
13988     }
13989     Roo.apply(this, config);
13990     
13991     if(!this.type){
13992         this.type = "auto";
13993     }
13994     
13995     var st = Roo.data.SortTypes;
13996     // named sortTypes are supported, here we look them up
13997     if(typeof this.sortType == "string"){
13998         this.sortType = st[this.sortType];
13999     }
14000     
14001     // set default sortType for strings and dates
14002     if(!this.sortType){
14003         switch(this.type){
14004             case "string":
14005                 this.sortType = st.asUCString;
14006                 break;
14007             case "date":
14008                 this.sortType = st.asDate;
14009                 break;
14010             default:
14011                 this.sortType = st.none;
14012         }
14013     }
14014
14015     // define once
14016     var stripRe = /[\$,%]/g;
14017
14018     // prebuilt conversion function for this field, instead of
14019     // switching every time we're reading a value
14020     if(!this.convert){
14021         var cv, dateFormat = this.dateFormat;
14022         switch(this.type){
14023             case "":
14024             case "auto":
14025             case undefined:
14026                 cv = function(v){ return v; };
14027                 break;
14028             case "string":
14029                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
14030                 break;
14031             case "int":
14032                 cv = function(v){
14033                     return v !== undefined && v !== null && v !== '' ?
14034                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14035                     };
14036                 break;
14037             case "float":
14038                 cv = function(v){
14039                     return v !== undefined && v !== null && v !== '' ?
14040                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14041                     };
14042                 break;
14043             case "bool":
14044             case "boolean":
14045                 cv = function(v){ return v === true || v === "true" || v == 1; };
14046                 break;
14047             case "date":
14048                 cv = function(v){
14049                     if(!v){
14050                         return '';
14051                     }
14052                     if(v instanceof Date){
14053                         return v;
14054                     }
14055                     if(dateFormat){
14056                         if(dateFormat == "timestamp"){
14057                             return new Date(v*1000);
14058                         }
14059                         return Date.parseDate(v, dateFormat);
14060                     }
14061                     var parsed = Date.parse(v);
14062                     return parsed ? new Date(parsed) : null;
14063                 };
14064              break;
14065             
14066         }
14067         this.convert = cv;
14068     }
14069 };
14070
14071 Roo.data.Field.prototype = {
14072     dateFormat: null,
14073     defaultValue: "",
14074     mapping: null,
14075     sortType : null,
14076     sortDir : "ASC"
14077 };/*
14078  * Based on:
14079  * Ext JS Library 1.1.1
14080  * Copyright(c) 2006-2007, Ext JS, LLC.
14081  *
14082  * Originally Released Under LGPL - original licence link has changed is not relivant.
14083  *
14084  * Fork - LGPL
14085  * <script type="text/javascript">
14086  */
14087  
14088 // Base class for reading structured data from a data source.  This class is intended to be
14089 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14090
14091 /**
14092  * @class Roo.data.DataReader
14093  * Base class for reading structured data from a data source.  This class is intended to be
14094  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14095  */
14096
14097 Roo.data.DataReader = function(meta, recordType){
14098     
14099     this.meta = meta;
14100     
14101     this.recordType = recordType instanceof Array ? 
14102         Roo.data.Record.create(recordType) : recordType;
14103 };
14104
14105 Roo.data.DataReader.prototype = {
14106     
14107     
14108     readerType : 'Data',
14109      /**
14110      * Create an empty record
14111      * @param {Object} data (optional) - overlay some values
14112      * @return {Roo.data.Record} record created.
14113      */
14114     newRow :  function(d) {
14115         var da =  {};
14116         this.recordType.prototype.fields.each(function(c) {
14117             switch( c.type) {
14118                 case 'int' : da[c.name] = 0; break;
14119                 case 'date' : da[c.name] = new Date(); break;
14120                 case 'float' : da[c.name] = 0.0; break;
14121                 case 'boolean' : da[c.name] = false; break;
14122                 default : da[c.name] = ""; break;
14123             }
14124             
14125         });
14126         return new this.recordType(Roo.apply(da, d));
14127     }
14128     
14129     
14130 };/*
14131  * Based on:
14132  * Ext JS Library 1.1.1
14133  * Copyright(c) 2006-2007, Ext JS, LLC.
14134  *
14135  * Originally Released Under LGPL - original licence link has changed is not relivant.
14136  *
14137  * Fork - LGPL
14138  * <script type="text/javascript">
14139  */
14140
14141 /**
14142  * @class Roo.data.DataProxy
14143  * @extends Roo.data.Observable
14144  * This class is an abstract base class for implementations which provide retrieval of
14145  * unformatted data objects.<br>
14146  * <p>
14147  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14148  * (of the appropriate type which knows how to parse the data object) to provide a block of
14149  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14150  * <p>
14151  * Custom implementations must implement the load method as described in
14152  * {@link Roo.data.HttpProxy#load}.
14153  */
14154 Roo.data.DataProxy = function(){
14155     this.addEvents({
14156         /**
14157          * @event beforeload
14158          * Fires before a network request is made to retrieve a data object.
14159          * @param {Object} This DataProxy object.
14160          * @param {Object} params The params parameter to the load function.
14161          */
14162         beforeload : true,
14163         /**
14164          * @event load
14165          * Fires before the load method's callback is called.
14166          * @param {Object} This DataProxy object.
14167          * @param {Object} o The data object.
14168          * @param {Object} arg The callback argument object passed to the load function.
14169          */
14170         load : true,
14171         /**
14172          * @event loadexception
14173          * Fires if an Exception occurs during data retrieval.
14174          * @param {Object} This DataProxy object.
14175          * @param {Object} o The data object.
14176          * @param {Object} arg The callback argument object passed to the load function.
14177          * @param {Object} e The Exception.
14178          */
14179         loadexception : true
14180     });
14181     Roo.data.DataProxy.superclass.constructor.call(this);
14182 };
14183
14184 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14185
14186     /**
14187      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14188      */
14189 /*
14190  * Based on:
14191  * Ext JS Library 1.1.1
14192  * Copyright(c) 2006-2007, Ext JS, LLC.
14193  *
14194  * Originally Released Under LGPL - original licence link has changed is not relivant.
14195  *
14196  * Fork - LGPL
14197  * <script type="text/javascript">
14198  */
14199 /**
14200  * @class Roo.data.MemoryProxy
14201  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14202  * to the Reader when its load method is called.
14203  * @constructor
14204  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14205  */
14206 Roo.data.MemoryProxy = function(data){
14207     if (data.data) {
14208         data = data.data;
14209     }
14210     Roo.data.MemoryProxy.superclass.constructor.call(this);
14211     this.data = data;
14212 };
14213
14214 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14215     
14216     /**
14217      * Load data from the requested source (in this case an in-memory
14218      * data object passed to the constructor), read the data object into
14219      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14220      * process that block using the passed callback.
14221      * @param {Object} params This parameter is not used by the MemoryProxy class.
14222      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14223      * object into a block of Roo.data.Records.
14224      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14225      * The function must be passed <ul>
14226      * <li>The Record block object</li>
14227      * <li>The "arg" argument from the load function</li>
14228      * <li>A boolean success indicator</li>
14229      * </ul>
14230      * @param {Object} scope The scope in which to call the callback
14231      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14232      */
14233     load : function(params, reader, callback, scope, arg){
14234         params = params || {};
14235         var result;
14236         try {
14237             result = reader.readRecords(params.data ? params.data :this.data);
14238         }catch(e){
14239             this.fireEvent("loadexception", this, arg, null, e);
14240             callback.call(scope, null, arg, false);
14241             return;
14242         }
14243         callback.call(scope, result, arg, true);
14244     },
14245     
14246     // private
14247     update : function(params, records){
14248         
14249     }
14250 });/*
14251  * Based on:
14252  * Ext JS Library 1.1.1
14253  * Copyright(c) 2006-2007, Ext JS, LLC.
14254  *
14255  * Originally Released Under LGPL - original licence link has changed is not relivant.
14256  *
14257  * Fork - LGPL
14258  * <script type="text/javascript">
14259  */
14260 /**
14261  * @class Roo.data.HttpProxy
14262  * @extends Roo.data.DataProxy
14263  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14264  * configured to reference a certain URL.<br><br>
14265  * <p>
14266  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14267  * from which the running page was served.<br><br>
14268  * <p>
14269  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14270  * <p>
14271  * Be aware that to enable the browser to parse an XML document, the server must set
14272  * the Content-Type header in the HTTP response to "text/xml".
14273  * @constructor
14274  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14275  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14276  * will be used to make the request.
14277  */
14278 Roo.data.HttpProxy = function(conn){
14279     Roo.data.HttpProxy.superclass.constructor.call(this);
14280     // is conn a conn config or a real conn?
14281     this.conn = conn;
14282     this.useAjax = !conn || !conn.events;
14283   
14284 };
14285
14286 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14287     // thse are take from connection...
14288     
14289     /**
14290      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14291      */
14292     /**
14293      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14294      * extra parameters to each request made by this object. (defaults to undefined)
14295      */
14296     /**
14297      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14298      *  to each request made by this object. (defaults to undefined)
14299      */
14300     /**
14301      * @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)
14302      */
14303     /**
14304      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14305      */
14306      /**
14307      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14308      * @type Boolean
14309      */
14310   
14311
14312     /**
14313      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14314      * @type Boolean
14315      */
14316     /**
14317      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14318      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14319      * a finer-grained basis than the DataProxy events.
14320      */
14321     getConnection : function(){
14322         return this.useAjax ? Roo.Ajax : this.conn;
14323     },
14324
14325     /**
14326      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14327      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14328      * process that block using the passed callback.
14329      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14330      * for the request to the remote server.
14331      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14332      * object into a block of Roo.data.Records.
14333      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14334      * The function must be passed <ul>
14335      * <li>The Record block object</li>
14336      * <li>The "arg" argument from the load function</li>
14337      * <li>A boolean success indicator</li>
14338      * </ul>
14339      * @param {Object} scope The scope in which to call the callback
14340      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14341      */
14342     load : function(params, reader, callback, scope, arg){
14343         if(this.fireEvent("beforeload", this, params) !== false){
14344             var  o = {
14345                 params : params || {},
14346                 request: {
14347                     callback : callback,
14348                     scope : scope,
14349                     arg : arg
14350                 },
14351                 reader: reader,
14352                 callback : this.loadResponse,
14353                 scope: this
14354             };
14355             if(this.useAjax){
14356                 Roo.applyIf(o, this.conn);
14357                 if(this.activeRequest){
14358                     Roo.Ajax.abort(this.activeRequest);
14359                 }
14360                 this.activeRequest = Roo.Ajax.request(o);
14361             }else{
14362                 this.conn.request(o);
14363             }
14364         }else{
14365             callback.call(scope||this, null, arg, false);
14366         }
14367     },
14368
14369     // private
14370     loadResponse : function(o, success, response){
14371         delete this.activeRequest;
14372         if(!success){
14373             this.fireEvent("loadexception", this, o, response);
14374             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14375             return;
14376         }
14377         var result;
14378         try {
14379             result = o.reader.read(response);
14380         }catch(e){
14381             this.fireEvent("loadexception", this, o, response, e);
14382             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14383             return;
14384         }
14385         
14386         this.fireEvent("load", this, o, o.request.arg);
14387         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14388     },
14389
14390     // private
14391     update : function(dataSet){
14392
14393     },
14394
14395     // private
14396     updateResponse : function(dataSet){
14397
14398     }
14399 });/*
14400  * Based on:
14401  * Ext JS Library 1.1.1
14402  * Copyright(c) 2006-2007, Ext JS, LLC.
14403  *
14404  * Originally Released Under LGPL - original licence link has changed is not relivant.
14405  *
14406  * Fork - LGPL
14407  * <script type="text/javascript">
14408  */
14409
14410 /**
14411  * @class Roo.data.ScriptTagProxy
14412  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14413  * other than the originating domain of the running page.<br><br>
14414  * <p>
14415  * <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
14416  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14417  * <p>
14418  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14419  * source code that is used as the source inside a &lt;script> tag.<br><br>
14420  * <p>
14421  * In order for the browser to process the returned data, the server must wrap the data object
14422  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14423  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14424  * depending on whether the callback name was passed:
14425  * <p>
14426  * <pre><code>
14427 boolean scriptTag = false;
14428 String cb = request.getParameter("callback");
14429 if (cb != null) {
14430     scriptTag = true;
14431     response.setContentType("text/javascript");
14432 } else {
14433     response.setContentType("application/x-json");
14434 }
14435 Writer out = response.getWriter();
14436 if (scriptTag) {
14437     out.write(cb + "(");
14438 }
14439 out.print(dataBlock.toJsonString());
14440 if (scriptTag) {
14441     out.write(");");
14442 }
14443 </pre></code>
14444  *
14445  * @constructor
14446  * @param {Object} config A configuration object.
14447  */
14448 Roo.data.ScriptTagProxy = function(config){
14449     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14450     Roo.apply(this, config);
14451     this.head = document.getElementsByTagName("head")[0];
14452 };
14453
14454 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14455
14456 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14457     /**
14458      * @cfg {String} url The URL from which to request the data object.
14459      */
14460     /**
14461      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14462      */
14463     timeout : 30000,
14464     /**
14465      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14466      * the server the name of the callback function set up by the load call to process the returned data object.
14467      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14468      * javascript output which calls this named function passing the data object as its only parameter.
14469      */
14470     callbackParam : "callback",
14471     /**
14472      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14473      * name to the request.
14474      */
14475     nocache : true,
14476
14477     /**
14478      * Load data from the configured URL, read the data object into
14479      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14480      * process that block using the passed callback.
14481      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14482      * for the request to the remote server.
14483      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14484      * object into a block of Roo.data.Records.
14485      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14486      * The function must be passed <ul>
14487      * <li>The Record block object</li>
14488      * <li>The "arg" argument from the load function</li>
14489      * <li>A boolean success indicator</li>
14490      * </ul>
14491      * @param {Object} scope The scope in which to call the callback
14492      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14493      */
14494     load : function(params, reader, callback, scope, arg){
14495         if(this.fireEvent("beforeload", this, params) !== false){
14496
14497             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14498
14499             var url = this.url;
14500             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14501             if(this.nocache){
14502                 url += "&_dc=" + (new Date().getTime());
14503             }
14504             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14505             var trans = {
14506                 id : transId,
14507                 cb : "stcCallback"+transId,
14508                 scriptId : "stcScript"+transId,
14509                 params : params,
14510                 arg : arg,
14511                 url : url,
14512                 callback : callback,
14513                 scope : scope,
14514                 reader : reader
14515             };
14516             var conn = this;
14517
14518             window[trans.cb] = function(o){
14519                 conn.handleResponse(o, trans);
14520             };
14521
14522             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14523
14524             if(this.autoAbort !== false){
14525                 this.abort();
14526             }
14527
14528             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14529
14530             var script = document.createElement("script");
14531             script.setAttribute("src", url);
14532             script.setAttribute("type", "text/javascript");
14533             script.setAttribute("id", trans.scriptId);
14534             this.head.appendChild(script);
14535
14536             this.trans = trans;
14537         }else{
14538             callback.call(scope||this, null, arg, false);
14539         }
14540     },
14541
14542     // private
14543     isLoading : function(){
14544         return this.trans ? true : false;
14545     },
14546
14547     /**
14548      * Abort the current server request.
14549      */
14550     abort : function(){
14551         if(this.isLoading()){
14552             this.destroyTrans(this.trans);
14553         }
14554     },
14555
14556     // private
14557     destroyTrans : function(trans, isLoaded){
14558         this.head.removeChild(document.getElementById(trans.scriptId));
14559         clearTimeout(trans.timeoutId);
14560         if(isLoaded){
14561             window[trans.cb] = undefined;
14562             try{
14563                 delete window[trans.cb];
14564             }catch(e){}
14565         }else{
14566             // if hasn't been loaded, wait for load to remove it to prevent script error
14567             window[trans.cb] = function(){
14568                 window[trans.cb] = undefined;
14569                 try{
14570                     delete window[trans.cb];
14571                 }catch(e){}
14572             };
14573         }
14574     },
14575
14576     // private
14577     handleResponse : function(o, trans){
14578         this.trans = false;
14579         this.destroyTrans(trans, true);
14580         var result;
14581         try {
14582             result = trans.reader.readRecords(o);
14583         }catch(e){
14584             this.fireEvent("loadexception", this, o, trans.arg, e);
14585             trans.callback.call(trans.scope||window, null, trans.arg, false);
14586             return;
14587         }
14588         this.fireEvent("load", this, o, trans.arg);
14589         trans.callback.call(trans.scope||window, result, trans.arg, true);
14590     },
14591
14592     // private
14593     handleFailure : function(trans){
14594         this.trans = false;
14595         this.destroyTrans(trans, false);
14596         this.fireEvent("loadexception", this, null, trans.arg);
14597         trans.callback.call(trans.scope||window, null, trans.arg, false);
14598     }
14599 });/*
14600  * Based on:
14601  * Ext JS Library 1.1.1
14602  * Copyright(c) 2006-2007, Ext JS, LLC.
14603  *
14604  * Originally Released Under LGPL - original licence link has changed is not relivant.
14605  *
14606  * Fork - LGPL
14607  * <script type="text/javascript">
14608  */
14609
14610 /**
14611  * @class Roo.data.JsonReader
14612  * @extends Roo.data.DataReader
14613  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14614  * based on mappings in a provided Roo.data.Record constructor.
14615  * 
14616  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14617  * in the reply previously. 
14618  * 
14619  * <p>
14620  * Example code:
14621  * <pre><code>
14622 var RecordDef = Roo.data.Record.create([
14623     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14624     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14625 ]);
14626 var myReader = new Roo.data.JsonReader({
14627     totalProperty: "results",    // The property which contains the total dataset size (optional)
14628     root: "rows",                // The property which contains an Array of row objects
14629     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14630 }, RecordDef);
14631 </code></pre>
14632  * <p>
14633  * This would consume a JSON file like this:
14634  * <pre><code>
14635 { 'results': 2, 'rows': [
14636     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14637     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14638 }
14639 </code></pre>
14640  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14641  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14642  * paged from the remote server.
14643  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14644  * @cfg {String} root name of the property which contains the Array of row objects.
14645  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14646  * @cfg {Array} fields Array of field definition objects
14647  * @constructor
14648  * Create a new JsonReader
14649  * @param {Object} meta Metadata configuration options
14650  * @param {Object} recordType Either an Array of field definition objects,
14651  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14652  */
14653 Roo.data.JsonReader = function(meta, recordType){
14654     
14655     meta = meta || {};
14656     // set some defaults:
14657     Roo.applyIf(meta, {
14658         totalProperty: 'total',
14659         successProperty : 'success',
14660         root : 'data',
14661         id : 'id'
14662     });
14663     
14664     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14665 };
14666 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14667     
14668     readerType : 'Json',
14669     
14670     /**
14671      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14672      * Used by Store query builder to append _requestMeta to params.
14673      * 
14674      */
14675     metaFromRemote : false,
14676     /**
14677      * This method is only used by a DataProxy which has retrieved data from a remote server.
14678      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14679      * @return {Object} data A data block which is used by an Roo.data.Store object as
14680      * a cache of Roo.data.Records.
14681      */
14682     read : function(response){
14683         var json = response.responseText;
14684        
14685         var o = /* eval:var:o */ eval("("+json+")");
14686         if(!o) {
14687             throw {message: "JsonReader.read: Json object not found"};
14688         }
14689         
14690         if(o.metaData){
14691             
14692             delete this.ef;
14693             this.metaFromRemote = true;
14694             this.meta = o.metaData;
14695             this.recordType = Roo.data.Record.create(o.metaData.fields);
14696             this.onMetaChange(this.meta, this.recordType, o);
14697         }
14698         return this.readRecords(o);
14699     },
14700
14701     // private function a store will implement
14702     onMetaChange : function(meta, recordType, o){
14703
14704     },
14705
14706     /**
14707          * @ignore
14708          */
14709     simpleAccess: function(obj, subsc) {
14710         return obj[subsc];
14711     },
14712
14713         /**
14714          * @ignore
14715          */
14716     getJsonAccessor: function(){
14717         var re = /[\[\.]/;
14718         return function(expr) {
14719             try {
14720                 return(re.test(expr))
14721                     ? new Function("obj", "return obj." + expr)
14722                     : function(obj){
14723                         return obj[expr];
14724                     };
14725             } catch(e){}
14726             return Roo.emptyFn;
14727         };
14728     }(),
14729
14730     /**
14731      * Create a data block containing Roo.data.Records from an XML document.
14732      * @param {Object} o An object which contains an Array of row objects in the property specified
14733      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14734      * which contains the total size of the dataset.
14735      * @return {Object} data A data block which is used by an Roo.data.Store object as
14736      * a cache of Roo.data.Records.
14737      */
14738     readRecords : function(o){
14739         /**
14740          * After any data loads, the raw JSON data is available for further custom processing.
14741          * @type Object
14742          */
14743         this.o = o;
14744         var s = this.meta, Record = this.recordType,
14745             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14746
14747 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14748         if (!this.ef) {
14749             if(s.totalProperty) {
14750                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14751                 }
14752                 if(s.successProperty) {
14753                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14754                 }
14755                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14756                 if (s.id) {
14757                         var g = this.getJsonAccessor(s.id);
14758                         this.getId = function(rec) {
14759                                 var r = g(rec);  
14760                                 return (r === undefined || r === "") ? null : r;
14761                         };
14762                 } else {
14763                         this.getId = function(){return null;};
14764                 }
14765             this.ef = [];
14766             for(var jj = 0; jj < fl; jj++){
14767                 f = fi[jj];
14768                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14769                 this.ef[jj] = this.getJsonAccessor(map);
14770             }
14771         }
14772
14773         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14774         if(s.totalProperty){
14775             var vt = parseInt(this.getTotal(o), 10);
14776             if(!isNaN(vt)){
14777                 totalRecords = vt;
14778             }
14779         }
14780         if(s.successProperty){
14781             var vs = this.getSuccess(o);
14782             if(vs === false || vs === 'false'){
14783                 success = false;
14784             }
14785         }
14786         var records = [];
14787         for(var i = 0; i < c; i++){
14788                 var n = root[i];
14789             var values = {};
14790             var id = this.getId(n);
14791             for(var j = 0; j < fl; j++){
14792                 f = fi[j];
14793             var v = this.ef[j](n);
14794             if (!f.convert) {
14795                 Roo.log('missing convert for ' + f.name);
14796                 Roo.log(f);
14797                 continue;
14798             }
14799             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14800             }
14801             var record = new Record(values, id);
14802             record.json = n;
14803             records[i] = record;
14804         }
14805         return {
14806             raw : o,
14807             success : success,
14808             records : records,
14809             totalRecords : totalRecords
14810         };
14811     },
14812     // used when loading children.. @see loadDataFromChildren
14813     toLoadData: function(rec)
14814     {
14815         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14816         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14817         return { data : data, total : data.length };
14818         
14819     }
14820 });/*
14821  * Based on:
14822  * Ext JS Library 1.1.1
14823  * Copyright(c) 2006-2007, Ext JS, LLC.
14824  *
14825  * Originally Released Under LGPL - original licence link has changed is not relivant.
14826  *
14827  * Fork - LGPL
14828  * <script type="text/javascript">
14829  */
14830
14831 /**
14832  * @class Roo.data.ArrayReader
14833  * @extends Roo.data.DataReader
14834  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14835  * Each element of that Array represents a row of data fields. The
14836  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14837  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14838  * <p>
14839  * Example code:.
14840  * <pre><code>
14841 var RecordDef = Roo.data.Record.create([
14842     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14843     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14844 ]);
14845 var myReader = new Roo.data.ArrayReader({
14846     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14847 }, RecordDef);
14848 </code></pre>
14849  * <p>
14850  * This would consume an Array like this:
14851  * <pre><code>
14852 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14853   </code></pre>
14854  
14855  * @constructor
14856  * Create a new JsonReader
14857  * @param {Object} meta Metadata configuration options.
14858  * @param {Object|Array} recordType Either an Array of field definition objects
14859  * 
14860  * @cfg {Array} fields Array of field definition objects
14861  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14862  * as specified to {@link Roo.data.Record#create},
14863  * or an {@link Roo.data.Record} object
14864  *
14865  * 
14866  * created using {@link Roo.data.Record#create}.
14867  */
14868 Roo.data.ArrayReader = function(meta, recordType)
14869 {    
14870     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14871 };
14872
14873 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14874     
14875       /**
14876      * Create a data block containing Roo.data.Records from an XML document.
14877      * @param {Object} o An Array of row objects which represents the dataset.
14878      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14879      * a cache of Roo.data.Records.
14880      */
14881     readRecords : function(o)
14882     {
14883         var sid = this.meta ? this.meta.id : null;
14884         var recordType = this.recordType, fields = recordType.prototype.fields;
14885         var records = [];
14886         var root = o;
14887         for(var i = 0; i < root.length; i++){
14888                 var n = root[i];
14889             var values = {};
14890             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14891             for(var j = 0, jlen = fields.length; j < jlen; j++){
14892                 var f = fields.items[j];
14893                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14894                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14895                 v = f.convert(v);
14896                 values[f.name] = v;
14897             }
14898             var record = new recordType(values, id);
14899             record.json = n;
14900             records[records.length] = record;
14901         }
14902         return {
14903             records : records,
14904             totalRecords : records.length
14905         };
14906     },
14907     // used when loading children.. @see loadDataFromChildren
14908     toLoadData: function(rec)
14909     {
14910         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14911         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14912         
14913     }
14914     
14915     
14916 });/*
14917  * - LGPL
14918  * * 
14919  */
14920
14921 /**
14922  * @class Roo.bootstrap.ComboBox
14923  * @extends Roo.bootstrap.TriggerField
14924  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14925  * @cfg {Boolean} append (true|false) default false
14926  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14927  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14928  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14929  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14930  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14931  * @cfg {Boolean} animate default true
14932  * @cfg {Boolean} emptyResultText only for touch device
14933  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14934  * @cfg {String} emptyTitle default ''
14935  * @cfg {Number} width fixed with? experimental
14936  * @constructor
14937  * Create a new ComboBox.
14938  * @param {Object} config Configuration options
14939  */
14940 Roo.bootstrap.ComboBox = function(config){
14941     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14942     this.addEvents({
14943         /**
14944          * @event expand
14945          * Fires when the dropdown list is expanded
14946         * @param {Roo.bootstrap.ComboBox} combo This combo box
14947         */
14948         'expand' : true,
14949         /**
14950          * @event collapse
14951          * Fires when the dropdown list is collapsed
14952         * @param {Roo.bootstrap.ComboBox} combo This combo box
14953         */
14954         'collapse' : true,
14955         /**
14956          * @event beforeselect
14957          * Fires before a list item is selected. Return false to cancel the selection.
14958         * @param {Roo.bootstrap.ComboBox} combo This combo box
14959         * @param {Roo.data.Record} record The data record returned from the underlying store
14960         * @param {Number} index The index of the selected item in the dropdown list
14961         */
14962         'beforeselect' : true,
14963         /**
14964          * @event select
14965          * Fires when a list item is selected
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14968         * @param {Number} index The index of the selected item in the dropdown list
14969         */
14970         'select' : true,
14971         /**
14972          * @event beforequery
14973          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14974          * The event object passed has these properties:
14975         * @param {Roo.bootstrap.ComboBox} combo This combo box
14976         * @param {String} query The query
14977         * @param {Boolean} forceAll true to force "all" query
14978         * @param {Boolean} cancel true to cancel the query
14979         * @param {Object} e The query event object
14980         */
14981         'beforequery': true,
14982          /**
14983          * @event add
14984          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14985         * @param {Roo.bootstrap.ComboBox} combo This combo box
14986         */
14987         'add' : true,
14988         /**
14989          * @event edit
14990          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14991         * @param {Roo.bootstrap.ComboBox} combo This combo box
14992         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14993         */
14994         'edit' : true,
14995         /**
14996          * @event remove
14997          * Fires when the remove value from the combobox array
14998         * @param {Roo.bootstrap.ComboBox} combo This combo box
14999         */
15000         'remove' : true,
15001         /**
15002          * @event afterremove
15003          * Fires when the remove value from the combobox array
15004         * @param {Roo.bootstrap.ComboBox} combo This combo box
15005         */
15006         'afterremove' : true,
15007         /**
15008          * @event specialfilter
15009          * Fires when specialfilter
15010             * @param {Roo.bootstrap.ComboBox} combo This combo box
15011             */
15012         'specialfilter' : true,
15013         /**
15014          * @event tick
15015          * Fires when tick the element
15016             * @param {Roo.bootstrap.ComboBox} combo This combo box
15017             */
15018         'tick' : true,
15019         /**
15020          * @event touchviewdisplay
15021          * Fires when touch view require special display (default is using displayField)
15022             * @param {Roo.bootstrap.ComboBox} combo This combo box
15023             * @param {Object} cfg set html .
15024             */
15025         'touchviewdisplay' : true
15026         
15027     });
15028     
15029     this.item = [];
15030     this.tickItems = [];
15031     
15032     this.selectedIndex = -1;
15033     if(this.mode == 'local'){
15034         if(config.queryDelay === undefined){
15035             this.queryDelay = 10;
15036         }
15037         if(config.minChars === undefined){
15038             this.minChars = 0;
15039         }
15040     }
15041 };
15042
15043 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15044      
15045     /**
15046      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15047      * rendering into an Roo.Editor, defaults to false)
15048      */
15049     /**
15050      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15051      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15052      */
15053     /**
15054      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15055      */
15056     /**
15057      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15058      * the dropdown list (defaults to undefined, with no header element)
15059      */
15060
15061      /**
15062      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15063      */
15064      
15065      /**
15066      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15067      */
15068     listWidth: undefined,
15069     /**
15070      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15071      * mode = 'remote' or 'text' if mode = 'local')
15072      */
15073     displayField: undefined,
15074     
15075     /**
15076      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15077      * mode = 'remote' or 'value' if mode = 'local'). 
15078      * Note: use of a valueField requires the user make a selection
15079      * in order for a value to be mapped.
15080      */
15081     valueField: undefined,
15082     /**
15083      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15084      */
15085     modalTitle : '',
15086     
15087     /**
15088      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15089      * field's data value (defaults to the underlying DOM element's name)
15090      */
15091     hiddenName: undefined,
15092     /**
15093      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15094      */
15095     listClass: '',
15096     /**
15097      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15098      */
15099     selectedClass: 'active',
15100     
15101     /**
15102      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15103      */
15104     shadow:'sides',
15105     /**
15106      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15107      * anchor positions (defaults to 'tl-bl')
15108      */
15109     listAlign: 'tl-bl?',
15110     /**
15111      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15112      */
15113     maxHeight: 300,
15114     /**
15115      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15116      * query specified by the allQuery config option (defaults to 'query')
15117      */
15118     triggerAction: 'query',
15119     /**
15120      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15121      * (defaults to 4, does not apply if editable = false)
15122      */
15123     minChars : 4,
15124     /**
15125      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15126      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15127      */
15128     typeAhead: false,
15129     /**
15130      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15131      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15132      */
15133     queryDelay: 500,
15134     /**
15135      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15136      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15137      */
15138     pageSize: 0,
15139     /**
15140      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15141      * when editable = true (defaults to false)
15142      */
15143     selectOnFocus:false,
15144     /**
15145      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15146      */
15147     queryParam: 'query',
15148     /**
15149      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15150      * when mode = 'remote' (defaults to 'Loading...')
15151      */
15152     loadingText: 'Loading...',
15153     /**
15154      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15155      */
15156     resizable: false,
15157     /**
15158      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15159      */
15160     handleHeight : 8,
15161     /**
15162      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15163      * traditional select (defaults to true)
15164      */
15165     editable: true,
15166     /**
15167      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15168      */
15169     allQuery: '',
15170     /**
15171      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15172      */
15173     mode: 'remote',
15174     /**
15175      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15176      * listWidth has a higher value)
15177      */
15178     minListWidth : 70,
15179     /**
15180      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15181      * allow the user to set arbitrary text into the field (defaults to false)
15182      */
15183     forceSelection:false,
15184     /**
15185      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15186      * if typeAhead = true (defaults to 250)
15187      */
15188     typeAheadDelay : 250,
15189     /**
15190      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15191      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15192      */
15193     valueNotFoundText : undefined,
15194     /**
15195      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15196      */
15197     blockFocus : false,
15198     
15199     /**
15200      * @cfg {Boolean} disableClear Disable showing of clear button.
15201      */
15202     disableClear : false,
15203     /**
15204      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15205      */
15206     alwaysQuery : false,
15207     
15208     /**
15209      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15210      */
15211     multiple : false,
15212     
15213     /**
15214      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15215      */
15216     invalidClass : "has-warning",
15217     
15218     /**
15219      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15220      */
15221     validClass : "has-success",
15222     
15223     /**
15224      * @cfg {Boolean} specialFilter (true|false) special filter default false
15225      */
15226     specialFilter : false,
15227     
15228     /**
15229      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15230      */
15231     mobileTouchView : true,
15232     
15233     /**
15234      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15235      */
15236     useNativeIOS : false,
15237     
15238     /**
15239      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15240      */
15241     mobile_restrict_height : false,
15242     
15243     ios_options : false,
15244     
15245     //private
15246     addicon : false,
15247     editicon: false,
15248     
15249     page: 0,
15250     hasQuery: false,
15251     append: false,
15252     loadNext: false,
15253     autoFocus : true,
15254     tickable : false,
15255     btnPosition : 'right',
15256     triggerList : true,
15257     showToggleBtn : true,
15258     animate : true,
15259     emptyResultText: 'Empty',
15260     triggerText : 'Select',
15261     emptyTitle : '',
15262     width : false,
15263     
15264     // element that contains real text value.. (when hidden is used..)
15265     
15266     getAutoCreate : function()
15267     {   
15268         var cfg = false;
15269         //render
15270         /*
15271          * Render classic select for iso
15272          */
15273         
15274         if(Roo.isIOS && this.useNativeIOS){
15275             cfg = this.getAutoCreateNativeIOS();
15276             return cfg;
15277         }
15278         
15279         /*
15280          * Touch Devices
15281          */
15282         
15283         if(Roo.isTouch && this.mobileTouchView){
15284             cfg = this.getAutoCreateTouchView();
15285             return cfg;;
15286         }
15287         
15288         /*
15289          *  Normal ComboBox
15290          */
15291         if(!this.tickable){
15292             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15293             return cfg;
15294         }
15295         
15296         /*
15297          *  ComboBox with tickable selections
15298          */
15299              
15300         var align = this.labelAlign || this.parentLabelAlign();
15301         
15302         cfg = {
15303             cls : 'form-group roo-combobox-tickable' //input-group
15304         };
15305         
15306         var btn_text_select = '';
15307         var btn_text_done = '';
15308         var btn_text_cancel = '';
15309         
15310         if (this.btn_text_show) {
15311             btn_text_select = 'Select';
15312             btn_text_done = 'Done';
15313             btn_text_cancel = 'Cancel'; 
15314         }
15315         
15316         var buttons = {
15317             tag : 'div',
15318             cls : 'tickable-buttons',
15319             cn : [
15320                 {
15321                     tag : 'button',
15322                     type : 'button',
15323                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15324                     //html : this.triggerText
15325                     html: btn_text_select
15326                 },
15327                 {
15328                     tag : 'button',
15329                     type : 'button',
15330                     name : 'ok',
15331                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15332                     //html : 'Done'
15333                     html: btn_text_done
15334                 },
15335                 {
15336                     tag : 'button',
15337                     type : 'button',
15338                     name : 'cancel',
15339                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15340                     //html : 'Cancel'
15341                     html: btn_text_cancel
15342                 }
15343             ]
15344         };
15345         
15346         if(this.editable){
15347             buttons.cn.unshift({
15348                 tag: 'input',
15349                 cls: 'roo-select2-search-field-input'
15350             });
15351         }
15352         
15353         var _this = this;
15354         
15355         Roo.each(buttons.cn, function(c){
15356             if (_this.size) {
15357                 c.cls += ' btn-' + _this.size;
15358             }
15359
15360             if (_this.disabled) {
15361                 c.disabled = true;
15362             }
15363         });
15364         
15365         var box = {
15366             tag: 'div',
15367             style : 'display: contents',
15368             cn: [
15369                 {
15370                     tag: 'input',
15371                     type : 'hidden',
15372                     cls: 'form-hidden-field'
15373                 },
15374                 {
15375                     tag: 'ul',
15376                     cls: 'roo-select2-choices',
15377                     cn:[
15378                         {
15379                             tag: 'li',
15380                             cls: 'roo-select2-search-field',
15381                             cn: [
15382                                 buttons
15383                             ]
15384                         }
15385                     ]
15386                 }
15387             ]
15388         };
15389         
15390         var combobox = {
15391             cls: 'roo-select2-container input-group roo-select2-container-multi',
15392             cn: [
15393                 
15394                 box
15395 //                {
15396 //                    tag: 'ul',
15397 //                    cls: 'typeahead typeahead-long dropdown-menu',
15398 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15399 //                }
15400             ]
15401         };
15402         
15403         if(this.hasFeedback && !this.allowBlank){
15404             
15405             var feedback = {
15406                 tag: 'span',
15407                 cls: 'glyphicon form-control-feedback'
15408             };
15409
15410             combobox.cn.push(feedback);
15411         }
15412         
15413         
15414         
15415         var indicator = {
15416             tag : 'i',
15417             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15418             tooltip : 'This field is required'
15419         };
15420         if (Roo.bootstrap.version == 4) {
15421             indicator = {
15422                 tag : 'i',
15423                 style : 'display:none'
15424             };
15425         }
15426         if (align ==='left' && this.fieldLabel.length) {
15427             
15428             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15429             
15430             cfg.cn = [
15431                 indicator,
15432                 {
15433                     tag: 'label',
15434                     'for' :  id,
15435                     cls : 'control-label col-form-label',
15436                     html : this.fieldLabel
15437
15438                 },
15439                 {
15440                     cls : "", 
15441                     cn: [
15442                         combobox
15443                     ]
15444                 }
15445
15446             ];
15447             
15448             var labelCfg = cfg.cn[1];
15449             var contentCfg = cfg.cn[2];
15450             
15451
15452             if(this.indicatorpos == 'right'){
15453                 
15454                 cfg.cn = [
15455                     {
15456                         tag: 'label',
15457                         'for' :  id,
15458                         cls : 'control-label col-form-label',
15459                         cn : [
15460                             {
15461                                 tag : 'span',
15462                                 html : this.fieldLabel
15463                             },
15464                             indicator
15465                         ]
15466                     },
15467                     {
15468                         cls : "",
15469                         cn: [
15470                             combobox
15471                         ]
15472                     }
15473
15474                 ];
15475                 
15476                 
15477                 
15478                 labelCfg = cfg.cn[0];
15479                 contentCfg = cfg.cn[1];
15480             
15481             }
15482             
15483             if(this.labelWidth > 12){
15484                 labelCfg.style = "width: " + this.labelWidth + 'px';
15485             }
15486             if(this.width * 1 > 0){
15487                 contentCfg.style = "width: " + this.width + 'px';
15488             }
15489             if(this.labelWidth < 13 && this.labelmd == 0){
15490                 this.labelmd = this.labelWidth;
15491             }
15492             
15493             if(this.labellg > 0){
15494                 labelCfg.cls += ' col-lg-' + this.labellg;
15495                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15496             }
15497             
15498             if(this.labelmd > 0){
15499                 labelCfg.cls += ' col-md-' + this.labelmd;
15500                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15501             }
15502             
15503             if(this.labelsm > 0){
15504                 labelCfg.cls += ' col-sm-' + this.labelsm;
15505                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15506             }
15507             
15508             if(this.labelxs > 0){
15509                 labelCfg.cls += ' col-xs-' + this.labelxs;
15510                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15511             }
15512                 
15513                 
15514         } else if ( this.fieldLabel.length) {
15515 //                Roo.log(" label");
15516                  cfg.cn = [
15517                    indicator,
15518                     {
15519                         tag: 'label',
15520                         //cls : 'input-group-addon',
15521                         html : this.fieldLabel
15522                     },
15523                     combobox
15524                 ];
15525                 
15526                 if(this.indicatorpos == 'right'){
15527                     cfg.cn = [
15528                         {
15529                             tag: 'label',
15530                             //cls : 'input-group-addon',
15531                             html : this.fieldLabel
15532                         },
15533                         indicator,
15534                         combobox
15535                     ];
15536                     
15537                 }
15538
15539         } else {
15540             
15541 //                Roo.log(" no label && no align");
15542                 cfg = combobox
15543                      
15544                 
15545         }
15546          
15547         var settings=this;
15548         ['xs','sm','md','lg'].map(function(size){
15549             if (settings[size]) {
15550                 cfg.cls += ' col-' + size + '-' + settings[size];
15551             }
15552         });
15553         
15554         return cfg;
15555         
15556     },
15557     
15558     _initEventsCalled : false,
15559     
15560     // private
15561     initEvents: function()
15562     {   
15563         if (this._initEventsCalled) { // as we call render... prevent looping...
15564             return;
15565         }
15566         this._initEventsCalled = true;
15567         
15568         if (!this.store) {
15569             throw "can not find store for combo";
15570         }
15571         
15572         this.indicator = this.indicatorEl();
15573         
15574         this.store = Roo.factory(this.store, Roo.data);
15575         this.store.parent = this;
15576         
15577         // if we are building from html. then this element is so complex, that we can not really
15578         // use the rendered HTML.
15579         // so we have to trash and replace the previous code.
15580         if (Roo.XComponent.build_from_html) {
15581             // remove this element....
15582             var e = this.el.dom, k=0;
15583             while (e ) { e = e.previousSibling;  ++k;}
15584
15585             this.el.remove();
15586             
15587             this.el=false;
15588             this.rendered = false;
15589             
15590             this.render(this.parent().getChildContainer(true), k);
15591         }
15592         
15593         if(Roo.isIOS && this.useNativeIOS){
15594             this.initIOSView();
15595             return;
15596         }
15597         
15598         /*
15599          * Touch Devices
15600          */
15601         
15602         if(Roo.isTouch && this.mobileTouchView){
15603             this.initTouchView();
15604             return;
15605         }
15606         
15607         if(this.tickable){
15608             this.initTickableEvents();
15609             return;
15610         }
15611         
15612         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15613         
15614         if(this.hiddenName){
15615             
15616             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15617             
15618             this.hiddenField.dom.value =
15619                 this.hiddenValue !== undefined ? this.hiddenValue :
15620                 this.value !== undefined ? this.value : '';
15621
15622             // prevent input submission
15623             this.el.dom.removeAttribute('name');
15624             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15625              
15626              
15627         }
15628         //if(Roo.isGecko){
15629         //    this.el.dom.setAttribute('autocomplete', 'off');
15630         //}
15631         
15632         var cls = 'x-combo-list';
15633         
15634         //this.list = new Roo.Layer({
15635         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15636         //});
15637         
15638         var _this = this;
15639         
15640         (function(){
15641             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15642             _this.list.setWidth(lw);
15643         }).defer(100);
15644         
15645         this.list.on('mouseover', this.onViewOver, this);
15646         this.list.on('mousemove', this.onViewMove, this);
15647         this.list.on('scroll', this.onViewScroll, this);
15648         
15649         /*
15650         this.list.swallowEvent('mousewheel');
15651         this.assetHeight = 0;
15652
15653         if(this.title){
15654             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15655             this.assetHeight += this.header.getHeight();
15656         }
15657
15658         this.innerList = this.list.createChild({cls:cls+'-inner'});
15659         this.innerList.on('mouseover', this.onViewOver, this);
15660         this.innerList.on('mousemove', this.onViewMove, this);
15661         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15662         
15663         if(this.allowBlank && !this.pageSize && !this.disableClear){
15664             this.footer = this.list.createChild({cls:cls+'-ft'});
15665             this.pageTb = new Roo.Toolbar(this.footer);
15666            
15667         }
15668         if(this.pageSize){
15669             this.footer = this.list.createChild({cls:cls+'-ft'});
15670             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15671                     {pageSize: this.pageSize});
15672             
15673         }
15674         
15675         if (this.pageTb && this.allowBlank && !this.disableClear) {
15676             var _this = this;
15677             this.pageTb.add(new Roo.Toolbar.Fill(), {
15678                 cls: 'x-btn-icon x-btn-clear',
15679                 text: '&#160;',
15680                 handler: function()
15681                 {
15682                     _this.collapse();
15683                     _this.clearValue();
15684                     _this.onSelect(false, -1);
15685                 }
15686             });
15687         }
15688         if (this.footer) {
15689             this.assetHeight += this.footer.getHeight();
15690         }
15691         */
15692             
15693         if(!this.tpl){
15694             this.tpl = Roo.bootstrap.version == 4 ?
15695                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15696                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15697         }
15698
15699         this.view = new Roo.View(this.list, this.tpl, {
15700             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15701         });
15702         //this.view.wrapEl.setDisplayed(false);
15703         this.view.on('click', this.onViewClick, this);
15704         
15705         
15706         this.store.on('beforeload', this.onBeforeLoad, this);
15707         this.store.on('load', this.onLoad, this);
15708         this.store.on('loadexception', this.onLoadException, this);
15709         /*
15710         if(this.resizable){
15711             this.resizer = new Roo.Resizable(this.list,  {
15712                pinned:true, handles:'se'
15713             });
15714             this.resizer.on('resize', function(r, w, h){
15715                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15716                 this.listWidth = w;
15717                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15718                 this.restrictHeight();
15719             }, this);
15720             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15721         }
15722         */
15723         if(!this.editable){
15724             this.editable = true;
15725             this.setEditable(false);
15726         }
15727         
15728         /*
15729         
15730         if (typeof(this.events.add.listeners) != 'undefined') {
15731             
15732             this.addicon = this.wrap.createChild(
15733                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15734        
15735             this.addicon.on('click', function(e) {
15736                 this.fireEvent('add', this);
15737             }, this);
15738         }
15739         if (typeof(this.events.edit.listeners) != 'undefined') {
15740             
15741             this.editicon = this.wrap.createChild(
15742                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15743             if (this.addicon) {
15744                 this.editicon.setStyle('margin-left', '40px');
15745             }
15746             this.editicon.on('click', function(e) {
15747                 
15748                 // we fire even  if inothing is selected..
15749                 this.fireEvent('edit', this, this.lastData );
15750                 
15751             }, this);
15752         }
15753         */
15754         
15755         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15756             "up" : function(e){
15757                 this.inKeyMode = true;
15758                 this.selectPrev();
15759             },
15760
15761             "down" : function(e){
15762                 if(!this.isExpanded()){
15763                     this.onTriggerClick();
15764                 }else{
15765                     this.inKeyMode = true;
15766                     this.selectNext();
15767                 }
15768             },
15769
15770             "enter" : function(e){
15771 //                this.onViewClick();
15772                 //return true;
15773                 this.collapse();
15774                 
15775                 if(this.fireEvent("specialkey", this, e)){
15776                     this.onViewClick(false);
15777                 }
15778                 
15779                 return true;
15780             },
15781
15782             "esc" : function(e){
15783                 this.collapse();
15784             },
15785
15786             "tab" : function(e){
15787                 this.collapse();
15788                 
15789                 if(this.fireEvent("specialkey", this, e)){
15790                     this.onViewClick(false);
15791                 }
15792                 
15793                 return true;
15794             },
15795
15796             scope : this,
15797
15798             doRelay : function(foo, bar, hname){
15799                 if(hname == 'down' || this.scope.isExpanded()){
15800                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15801                 }
15802                 return true;
15803             },
15804
15805             forceKeyDown: true
15806         });
15807         
15808         
15809         this.queryDelay = Math.max(this.queryDelay || 10,
15810                 this.mode == 'local' ? 10 : 250);
15811         
15812         
15813         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15814         
15815         if(this.typeAhead){
15816             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15817         }
15818         if(this.editable !== false){
15819             this.inputEl().on("keyup", this.onKeyUp, this);
15820         }
15821         if(this.forceSelection){
15822             this.inputEl().on('blur', this.doForce, this);
15823         }
15824         
15825         if(this.multiple){
15826             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15827             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15828         }
15829     },
15830     
15831     initTickableEvents: function()
15832     {   
15833         this.createList();
15834         
15835         if(this.hiddenName){
15836             
15837             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15838             
15839             this.hiddenField.dom.value =
15840                 this.hiddenValue !== undefined ? this.hiddenValue :
15841                 this.value !== undefined ? this.value : '';
15842
15843             // prevent input submission
15844             this.el.dom.removeAttribute('name');
15845             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15846              
15847              
15848         }
15849         
15850 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15851         
15852         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15853         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15854         if(this.triggerList){
15855             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15856         }
15857          
15858         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15859         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15860         
15861         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15862         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15863         
15864         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15865         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15866         
15867         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15868         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15869         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15870         
15871         this.okBtn.hide();
15872         this.cancelBtn.hide();
15873         
15874         var _this = this;
15875         
15876         (function(){
15877             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15878             _this.list.setWidth(lw);
15879         }).defer(100);
15880         
15881         this.list.on('mouseover', this.onViewOver, this);
15882         this.list.on('mousemove', this.onViewMove, this);
15883         
15884         this.list.on('scroll', this.onViewScroll, this);
15885         
15886         if(!this.tpl){
15887             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15888                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15889         }
15890
15891         this.view = new Roo.View(this.list, this.tpl, {
15892             singleSelect:true,
15893             tickable:true,
15894             parent:this,
15895             store: this.store,
15896             selectedClass: this.selectedClass
15897         });
15898         
15899         //this.view.wrapEl.setDisplayed(false);
15900         this.view.on('click', this.onViewClick, this);
15901         
15902         
15903         
15904         this.store.on('beforeload', this.onBeforeLoad, this);
15905         this.store.on('load', this.onLoad, this);
15906         this.store.on('loadexception', this.onLoadException, this);
15907         
15908         if(this.editable){
15909             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15910                 "up" : function(e){
15911                     this.inKeyMode = true;
15912                     this.selectPrev();
15913                 },
15914
15915                 "down" : function(e){
15916                     this.inKeyMode = true;
15917                     this.selectNext();
15918                 },
15919
15920                 "enter" : function(e){
15921                     if(this.fireEvent("specialkey", this, e)){
15922                         this.onViewClick(false);
15923                     }
15924                     
15925                     return true;
15926                 },
15927
15928                 "esc" : function(e){
15929                     this.onTickableFooterButtonClick(e, false, false);
15930                 },
15931
15932                 "tab" : function(e){
15933                     this.fireEvent("specialkey", this, e);
15934                     
15935                     this.onTickableFooterButtonClick(e, false, false);
15936                     
15937                     return true;
15938                 },
15939
15940                 scope : this,
15941
15942                 doRelay : function(e, fn, key){
15943                     if(this.scope.isExpanded()){
15944                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15945                     }
15946                     return true;
15947                 },
15948
15949                 forceKeyDown: true
15950             });
15951         }
15952         
15953         this.queryDelay = Math.max(this.queryDelay || 10,
15954                 this.mode == 'local' ? 10 : 250);
15955         
15956         
15957         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15958         
15959         if(this.typeAhead){
15960             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15961         }
15962         
15963         if(this.editable !== false){
15964             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15965         }
15966         
15967         this.indicator = this.indicatorEl();
15968         
15969         if(this.indicator){
15970             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15971             this.indicator.hide();
15972         }
15973         
15974     },
15975
15976     onDestroy : function(){
15977         if(this.view){
15978             this.view.setStore(null);
15979             this.view.el.removeAllListeners();
15980             this.view.el.remove();
15981             this.view.purgeListeners();
15982         }
15983         if(this.list){
15984             this.list.dom.innerHTML  = '';
15985         }
15986         
15987         if(this.store){
15988             this.store.un('beforeload', this.onBeforeLoad, this);
15989             this.store.un('load', this.onLoad, this);
15990             this.store.un('loadexception', this.onLoadException, this);
15991         }
15992         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15993     },
15994
15995     // private
15996     fireKey : function(e){
15997         if(e.isNavKeyPress() && !this.list.isVisible()){
15998             this.fireEvent("specialkey", this, e);
15999         }
16000     },
16001
16002     // private
16003     onResize: function(w, h)
16004     {
16005         
16006         
16007 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
16008 //        
16009 //        if(typeof w != 'number'){
16010 //            // we do not handle it!?!?
16011 //            return;
16012 //        }
16013 //        var tw = this.trigger.getWidth();
16014 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
16015 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
16016 //        var x = w - tw;
16017 //        this.inputEl().setWidth( this.adjustWidth('input', x));
16018 //            
16019 //        //this.trigger.setStyle('left', x+'px');
16020 //        
16021 //        if(this.list && this.listWidth === undefined){
16022 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
16023 //            this.list.setWidth(lw);
16024 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
16025 //        }
16026         
16027     
16028         
16029     },
16030
16031     /**
16032      * Allow or prevent the user from directly editing the field text.  If false is passed,
16033      * the user will only be able to select from the items defined in the dropdown list.  This method
16034      * is the runtime equivalent of setting the 'editable' config option at config time.
16035      * @param {Boolean} value True to allow the user to directly edit the field text
16036      */
16037     setEditable : function(value){
16038         if(value == this.editable){
16039             return;
16040         }
16041         this.editable = value;
16042         if(!value){
16043             this.inputEl().dom.setAttribute('readOnly', true);
16044             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16045             this.inputEl().addClass('x-combo-noedit');
16046         }else{
16047             this.inputEl().dom.setAttribute('readOnly', false);
16048             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16049             this.inputEl().removeClass('x-combo-noedit');
16050         }
16051     },
16052
16053     // private
16054     
16055     onBeforeLoad : function(combo,opts){
16056         if(!this.hasFocus){
16057             return;
16058         }
16059          if (!opts.add) {
16060             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16061          }
16062         this.restrictHeight();
16063         this.selectedIndex = -1;
16064     },
16065
16066     // private
16067     onLoad : function(){
16068         
16069         this.hasQuery = false;
16070         
16071         if(!this.hasFocus){
16072             return;
16073         }
16074         
16075         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16076             this.loading.hide();
16077         }
16078         
16079         if(this.store.getCount() > 0){
16080             
16081             this.expand();
16082             this.restrictHeight();
16083             if(this.lastQuery == this.allQuery){
16084                 if(this.editable && !this.tickable){
16085                     this.inputEl().dom.select();
16086                 }
16087                 
16088                 if(
16089                     !this.selectByValue(this.value, true) &&
16090                     this.autoFocus && 
16091                     (
16092                         !this.store.lastOptions ||
16093                         typeof(this.store.lastOptions.add) == 'undefined' || 
16094                         this.store.lastOptions.add != true
16095                     )
16096                 ){
16097                     this.select(0, true);
16098                 }
16099             }else{
16100                 if(this.autoFocus){
16101                     this.selectNext();
16102                 }
16103                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16104                     this.taTask.delay(this.typeAheadDelay);
16105                 }
16106             }
16107         }else{
16108             this.onEmptyResults();
16109         }
16110         
16111         //this.el.focus();
16112     },
16113     // private
16114     onLoadException : function()
16115     {
16116         this.hasQuery = false;
16117         
16118         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16119             this.loading.hide();
16120         }
16121         
16122         if(this.tickable && this.editable){
16123             return;
16124         }
16125         
16126         this.collapse();
16127         // only causes errors at present
16128         //Roo.log(this.store.reader.jsonData);
16129         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16130             // fixme
16131             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16132         //}
16133         
16134         
16135     },
16136     // private
16137     onTypeAhead : function(){
16138         if(this.store.getCount() > 0){
16139             var r = this.store.getAt(0);
16140             var newValue = r.data[this.displayField];
16141             var len = newValue.length;
16142             var selStart = this.getRawValue().length;
16143             
16144             if(selStart != len){
16145                 this.setRawValue(newValue);
16146                 this.selectText(selStart, newValue.length);
16147             }
16148         }
16149     },
16150
16151     // private
16152     onSelect : function(record, index){
16153         
16154         if(this.fireEvent('beforeselect', this, record, index) !== false){
16155         
16156             this.setFromData(index > -1 ? record.data : false);
16157             
16158             this.collapse();
16159             this.fireEvent('select', this, record, index);
16160         }
16161     },
16162
16163     /**
16164      * Returns the currently selected field value or empty string if no value is set.
16165      * @return {String} value The selected value
16166      */
16167     getValue : function()
16168     {
16169         if(Roo.isIOS && this.useNativeIOS){
16170             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16171         }
16172         
16173         if(this.multiple){
16174             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16175         }
16176         
16177         if(this.valueField){
16178             return typeof this.value != 'undefined' ? this.value : '';
16179         }else{
16180             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16181         }
16182     },
16183     
16184     getRawValue : function()
16185     {
16186         if(Roo.isIOS && this.useNativeIOS){
16187             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16188         }
16189         
16190         var v = this.inputEl().getValue();
16191         
16192         return v;
16193     },
16194
16195     /**
16196      * Clears any text/value currently set in the field
16197      */
16198     clearValue : function(){
16199         
16200         if(this.hiddenField){
16201             this.hiddenField.dom.value = '';
16202         }
16203         this.value = '';
16204         this.setRawValue('');
16205         this.lastSelectionText = '';
16206         this.lastData = false;
16207         
16208         var close = this.closeTriggerEl();
16209         
16210         if(close){
16211             close.hide();
16212         }
16213         
16214         this.validate();
16215         
16216     },
16217
16218     /**
16219      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16220      * will be displayed in the field.  If the value does not match the data value of an existing item,
16221      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16222      * Otherwise the field will be blank (although the value will still be set).
16223      * @param {String} value The value to match
16224      */
16225     setValue : function(v)
16226     {
16227         if(Roo.isIOS && this.useNativeIOS){
16228             this.setIOSValue(v);
16229             return;
16230         }
16231         
16232         if(this.multiple){
16233             this.syncValue();
16234             return;
16235         }
16236         
16237         var text = v;
16238         if(this.valueField){
16239             var r = this.findRecord(this.valueField, v);
16240             if(r){
16241                 text = r.data[this.displayField];
16242             }else if(this.valueNotFoundText !== undefined){
16243                 text = this.valueNotFoundText;
16244             }
16245         }
16246         this.lastSelectionText = text;
16247         if(this.hiddenField){
16248             this.hiddenField.dom.value = v;
16249         }
16250         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16251         this.value = v;
16252         
16253         var close = this.closeTriggerEl();
16254         
16255         if(close){
16256             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16257         }
16258         
16259         this.validate();
16260     },
16261     /**
16262      * @property {Object} the last set data for the element
16263      */
16264     
16265     lastData : false,
16266     /**
16267      * Sets the value of the field based on a object which is related to the record format for the store.
16268      * @param {Object} value the value to set as. or false on reset?
16269      */
16270     setFromData : function(o){
16271         
16272         if(this.multiple){
16273             this.addItem(o);
16274             return;
16275         }
16276             
16277         var dv = ''; // display value
16278         var vv = ''; // value value..
16279         this.lastData = o;
16280         if (this.displayField) {
16281             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16282         } else {
16283             // this is an error condition!!!
16284             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16285         }
16286         
16287         if(this.valueField){
16288             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16289         }
16290         
16291         var close = this.closeTriggerEl();
16292         
16293         if(close){
16294             if(dv.length || vv * 1 > 0){
16295                 close.show() ;
16296                 this.blockFocus=true;
16297             } else {
16298                 close.hide();
16299             }             
16300         }
16301         
16302         if(this.hiddenField){
16303             this.hiddenField.dom.value = vv;
16304             
16305             this.lastSelectionText = dv;
16306             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16307             this.value = vv;
16308             return;
16309         }
16310         // no hidden field.. - we store the value in 'value', but still display
16311         // display field!!!!
16312         this.lastSelectionText = dv;
16313         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16314         this.value = vv;
16315         
16316         
16317         
16318     },
16319     // private
16320     reset : function(){
16321         // overridden so that last data is reset..
16322         
16323         if(this.multiple){
16324             this.clearItem();
16325             return;
16326         }
16327         
16328         this.setValue(this.originalValue);
16329         //this.clearInvalid();
16330         this.lastData = false;
16331         if (this.view) {
16332             this.view.clearSelections();
16333         }
16334         
16335         this.validate();
16336     },
16337     // private
16338     findRecord : function(prop, value){
16339         var record;
16340         if(this.store.getCount() > 0){
16341             this.store.each(function(r){
16342                 if(r.data[prop] == value){
16343                     record = r;
16344                     return false;
16345                 }
16346                 return true;
16347             });
16348         }
16349         return record;
16350     },
16351     
16352     getName: function()
16353     {
16354         // returns hidden if it's set..
16355         if (!this.rendered) {return ''};
16356         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16357         
16358     },
16359     // private
16360     onViewMove : function(e, t){
16361         this.inKeyMode = false;
16362     },
16363
16364     // private
16365     onViewOver : function(e, t){
16366         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16367             return;
16368         }
16369         var item = this.view.findItemFromChild(t);
16370         
16371         if(item){
16372             var index = this.view.indexOf(item);
16373             this.select(index, false);
16374         }
16375     },
16376
16377     // private
16378     onViewClick : function(view, doFocus, el, e)
16379     {
16380         var index = this.view.getSelectedIndexes()[0];
16381         
16382         var r = this.store.getAt(index);
16383         
16384         if(this.tickable){
16385             
16386             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16387                 return;
16388             }
16389             
16390             var rm = false;
16391             var _this = this;
16392             
16393             Roo.each(this.tickItems, function(v,k){
16394                 
16395                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16396                     Roo.log(v);
16397                     _this.tickItems.splice(k, 1);
16398                     
16399                     if(typeof(e) == 'undefined' && view == false){
16400                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16401                     }
16402                     
16403                     rm = true;
16404                     return;
16405                 }
16406             });
16407             
16408             if(rm){
16409                 return;
16410             }
16411             
16412             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16413                 this.tickItems.push(r.data);
16414             }
16415             
16416             if(typeof(e) == 'undefined' && view == false){
16417                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16418             }
16419                     
16420             return;
16421         }
16422         
16423         if(r){
16424             this.onSelect(r, index);
16425         }
16426         if(doFocus !== false && !this.blockFocus){
16427             this.inputEl().focus();
16428         }
16429     },
16430
16431     // private
16432     restrictHeight : function(){
16433         //this.innerList.dom.style.height = '';
16434         //var inner = this.innerList.dom;
16435         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16436         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16437         //this.list.beginUpdate();
16438         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16439         this.list.alignTo(this.inputEl(), this.listAlign);
16440         this.list.alignTo(this.inputEl(), this.listAlign);
16441         //this.list.endUpdate();
16442     },
16443
16444     // private
16445     onEmptyResults : function(){
16446         
16447         if(this.tickable && this.editable){
16448             this.hasFocus = false;
16449             this.restrictHeight();
16450             return;
16451         }
16452         
16453         this.collapse();
16454     },
16455
16456     /**
16457      * Returns true if the dropdown list is expanded, else false.
16458      */
16459     isExpanded : function(){
16460         return this.list.isVisible();
16461     },
16462
16463     /**
16464      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16465      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16466      * @param {String} value The data value of the item to select
16467      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16468      * selected item if it is not currently in view (defaults to true)
16469      * @return {Boolean} True if the value matched an item in the list, else false
16470      */
16471     selectByValue : function(v, scrollIntoView){
16472         if(v !== undefined && v !== null){
16473             var r = this.findRecord(this.valueField || this.displayField, v);
16474             if(r){
16475                 this.select(this.store.indexOf(r), scrollIntoView);
16476                 return true;
16477             }
16478         }
16479         return false;
16480     },
16481
16482     /**
16483      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16484      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16485      * @param {Number} index The zero-based index of the list item to select
16486      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16487      * selected item if it is not currently in view (defaults to true)
16488      */
16489     select : function(index, scrollIntoView){
16490         this.selectedIndex = index;
16491         this.view.select(index);
16492         if(scrollIntoView !== false){
16493             var el = this.view.getNode(index);
16494             /*
16495              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16496              */
16497             if(el){
16498                 this.list.scrollChildIntoView(el, false);
16499             }
16500         }
16501     },
16502
16503     // private
16504     selectNext : function(){
16505         var ct = this.store.getCount();
16506         if(ct > 0){
16507             if(this.selectedIndex == -1){
16508                 this.select(0);
16509             }else if(this.selectedIndex < ct-1){
16510                 this.select(this.selectedIndex+1);
16511             }
16512         }
16513     },
16514
16515     // private
16516     selectPrev : function(){
16517         var ct = this.store.getCount();
16518         if(ct > 0){
16519             if(this.selectedIndex == -1){
16520                 this.select(0);
16521             }else if(this.selectedIndex != 0){
16522                 this.select(this.selectedIndex-1);
16523             }
16524         }
16525     },
16526
16527     // private
16528     onKeyUp : function(e){
16529         if(this.editable !== false && !e.isSpecialKey()){
16530             this.lastKey = e.getKey();
16531             this.dqTask.delay(this.queryDelay);
16532         }
16533     },
16534
16535     // private
16536     validateBlur : function(){
16537         return !this.list || !this.list.isVisible();   
16538     },
16539
16540     // private
16541     initQuery : function(){
16542         
16543         var v = this.getRawValue();
16544         
16545         if(this.tickable && this.editable){
16546             v = this.tickableInputEl().getValue();
16547         }
16548         
16549         this.doQuery(v);
16550     },
16551
16552     // private
16553     doForce : function(){
16554         if(this.inputEl().dom.value.length > 0){
16555             this.inputEl().dom.value =
16556                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16557              
16558         }
16559     },
16560
16561     /**
16562      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16563      * query allowing the query action to be canceled if needed.
16564      * @param {String} query The SQL query to execute
16565      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16566      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16567      * saved in the current store (defaults to false)
16568      */
16569     doQuery : function(q, forceAll){
16570         
16571         if(q === undefined || q === null){
16572             q = '';
16573         }
16574         var qe = {
16575             query: q,
16576             forceAll: forceAll,
16577             combo: this,
16578             cancel:false
16579         };
16580         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16581             return false;
16582         }
16583         q = qe.query;
16584         
16585         forceAll = qe.forceAll;
16586         if(forceAll === true || (q.length >= this.minChars)){
16587             
16588             this.hasQuery = true;
16589             
16590             if(this.lastQuery != q || this.alwaysQuery){
16591                 this.lastQuery = q;
16592                 if(this.mode == 'local'){
16593                     this.selectedIndex = -1;
16594                     if(forceAll){
16595                         this.store.clearFilter();
16596                     }else{
16597                         
16598                         if(this.specialFilter){
16599                             this.fireEvent('specialfilter', this);
16600                             this.onLoad();
16601                             return;
16602                         }
16603                         
16604                         this.store.filter(this.displayField, q);
16605                     }
16606                     
16607                     this.store.fireEvent("datachanged", this.store);
16608                     
16609                     this.onLoad();
16610                     
16611                     
16612                 }else{
16613                     
16614                     this.store.baseParams[this.queryParam] = q;
16615                     
16616                     var options = {params : this.getParams(q)};
16617                     
16618                     if(this.loadNext){
16619                         options.add = true;
16620                         options.params.start = this.page * this.pageSize;
16621                     }
16622                     
16623                     this.store.load(options);
16624                     
16625                     /*
16626                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16627                      *  we should expand the list on onLoad
16628                      *  so command out it
16629                      */
16630 //                    this.expand();
16631                 }
16632             }else{
16633                 this.selectedIndex = -1;
16634                 this.onLoad();   
16635             }
16636         }
16637         
16638         this.loadNext = false;
16639     },
16640     
16641     // private
16642     getParams : function(q){
16643         var p = {};
16644         //p[this.queryParam] = q;
16645         
16646         if(this.pageSize){
16647             p.start = 0;
16648             p.limit = this.pageSize;
16649         }
16650         return p;
16651     },
16652
16653     /**
16654      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16655      */
16656     collapse : function(){
16657         if(!this.isExpanded()){
16658             return;
16659         }
16660         
16661         this.list.hide();
16662         
16663         this.hasFocus = false;
16664         
16665         if(this.tickable){
16666             this.okBtn.hide();
16667             this.cancelBtn.hide();
16668             this.trigger.show();
16669             
16670             if(this.editable){
16671                 this.tickableInputEl().dom.value = '';
16672                 this.tickableInputEl().blur();
16673             }
16674             
16675         }
16676         
16677         Roo.get(document).un('mousedown', this.collapseIf, this);
16678         Roo.get(document).un('mousewheel', this.collapseIf, this);
16679         if (!this.editable) {
16680             Roo.get(document).un('keydown', this.listKeyPress, this);
16681         }
16682         this.fireEvent('collapse', this);
16683         
16684         this.validate();
16685     },
16686
16687     // private
16688     collapseIf : function(e){
16689         var in_combo  = e.within(this.el);
16690         var in_list =  e.within(this.list);
16691         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16692         
16693         if (in_combo || in_list || is_list) {
16694             //e.stopPropagation();
16695             return;
16696         }
16697         
16698         if(this.tickable){
16699             this.onTickableFooterButtonClick(e, false, false);
16700         }
16701
16702         this.collapse();
16703         
16704     },
16705
16706     /**
16707      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16708      */
16709     expand : function(){
16710        
16711         if(this.isExpanded() || !this.hasFocus){
16712             return;
16713         }
16714         
16715         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16716         this.list.setWidth(lw);
16717         
16718         Roo.log('expand');
16719         
16720         this.list.show();
16721         
16722         this.restrictHeight();
16723         
16724         if(this.tickable){
16725             
16726             this.tickItems = Roo.apply([], this.item);
16727             
16728             this.okBtn.show();
16729             this.cancelBtn.show();
16730             this.trigger.hide();
16731             
16732             if(this.editable){
16733                 this.tickableInputEl().focus();
16734             }
16735             
16736         }
16737         
16738         Roo.get(document).on('mousedown', this.collapseIf, this);
16739         Roo.get(document).on('mousewheel', this.collapseIf, this);
16740         if (!this.editable) {
16741             Roo.get(document).on('keydown', this.listKeyPress, this);
16742         }
16743         
16744         this.fireEvent('expand', this);
16745     },
16746
16747     // private
16748     // Implements the default empty TriggerField.onTriggerClick function
16749     onTriggerClick : function(e)
16750     {
16751         Roo.log('trigger click');
16752         
16753         if(this.disabled || !this.triggerList){
16754             return;
16755         }
16756         
16757         this.page = 0;
16758         this.loadNext = false;
16759         
16760         if(this.isExpanded()){
16761             this.collapse();
16762             if (!this.blockFocus) {
16763                 this.inputEl().focus();
16764             }
16765             
16766         }else {
16767             this.hasFocus = true;
16768             if(this.triggerAction == 'all') {
16769                 this.doQuery(this.allQuery, true);
16770             } else {
16771                 this.doQuery(this.getRawValue());
16772             }
16773             if (!this.blockFocus) {
16774                 this.inputEl().focus();
16775             }
16776         }
16777     },
16778     
16779     onTickableTriggerClick : function(e)
16780     {
16781         if(this.disabled){
16782             return;
16783         }
16784         
16785         this.page = 0;
16786         this.loadNext = false;
16787         this.hasFocus = true;
16788         
16789         if(this.triggerAction == 'all') {
16790             this.doQuery(this.allQuery, true);
16791         } else {
16792             this.doQuery(this.getRawValue());
16793         }
16794     },
16795     
16796     onSearchFieldClick : function(e)
16797     {
16798         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16799             this.onTickableFooterButtonClick(e, false, false);
16800             return;
16801         }
16802         
16803         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16804             return;
16805         }
16806         
16807         this.page = 0;
16808         this.loadNext = false;
16809         this.hasFocus = true;
16810         
16811         if(this.triggerAction == 'all') {
16812             this.doQuery(this.allQuery, true);
16813         } else {
16814             this.doQuery(this.getRawValue());
16815         }
16816     },
16817     
16818     listKeyPress : function(e)
16819     {
16820         //Roo.log('listkeypress');
16821         // scroll to first matching element based on key pres..
16822         if (e.isSpecialKey()) {
16823             return false;
16824         }
16825         var k = String.fromCharCode(e.getKey()).toUpperCase();
16826         //Roo.log(k);
16827         var match  = false;
16828         var csel = this.view.getSelectedNodes();
16829         var cselitem = false;
16830         if (csel.length) {
16831             var ix = this.view.indexOf(csel[0]);
16832             cselitem  = this.store.getAt(ix);
16833             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16834                 cselitem = false;
16835             }
16836             
16837         }
16838         
16839         this.store.each(function(v) { 
16840             if (cselitem) {
16841                 // start at existing selection.
16842                 if (cselitem.id == v.id) {
16843                     cselitem = false;
16844                 }
16845                 return true;
16846             }
16847                 
16848             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16849                 match = this.store.indexOf(v);
16850                 return false;
16851             }
16852             return true;
16853         }, this);
16854         
16855         if (match === false) {
16856             return true; // no more action?
16857         }
16858         // scroll to?
16859         this.view.select(match);
16860         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16861         sn.scrollIntoView(sn.dom.parentNode, false);
16862     },
16863     
16864     onViewScroll : function(e, t){
16865         
16866         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){
16867             return;
16868         }
16869         
16870         this.hasQuery = true;
16871         
16872         this.loading = this.list.select('.loading', true).first();
16873         
16874         if(this.loading === null){
16875             this.list.createChild({
16876                 tag: 'div',
16877                 cls: 'loading roo-select2-more-results roo-select2-active',
16878                 html: 'Loading more results...'
16879             });
16880             
16881             this.loading = this.list.select('.loading', true).first();
16882             
16883             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16884             
16885             this.loading.hide();
16886         }
16887         
16888         this.loading.show();
16889         
16890         var _combo = this;
16891         
16892         this.page++;
16893         this.loadNext = true;
16894         
16895         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16896         
16897         return;
16898     },
16899     
16900     addItem : function(o)
16901     {   
16902         var dv = ''; // display value
16903         
16904         if (this.displayField) {
16905             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16906         } else {
16907             // this is an error condition!!!
16908             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16909         }
16910         
16911         if(!dv.length){
16912             return;
16913         }
16914         
16915         var choice = this.choices.createChild({
16916             tag: 'li',
16917             cls: 'roo-select2-search-choice',
16918             cn: [
16919                 {
16920                     tag: 'div',
16921                     html: dv
16922                 },
16923                 {
16924                     tag: 'a',
16925                     href: '#',
16926                     cls: 'roo-select2-search-choice-close fa fa-times',
16927                     tabindex: '-1'
16928                 }
16929             ]
16930             
16931         }, this.searchField);
16932         
16933         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16934         
16935         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16936         
16937         this.item.push(o);
16938         
16939         this.lastData = o;
16940         
16941         this.syncValue();
16942         
16943         this.inputEl().dom.value = '';
16944         
16945         this.validate();
16946     },
16947     
16948     onRemoveItem : function(e, _self, o)
16949     {
16950         e.preventDefault();
16951         
16952         this.lastItem = Roo.apply([], this.item);
16953         
16954         var index = this.item.indexOf(o.data) * 1;
16955         
16956         if( index < 0){
16957             Roo.log('not this item?!');
16958             return;
16959         }
16960         
16961         this.item.splice(index, 1);
16962         o.item.remove();
16963         
16964         this.syncValue();
16965         
16966         this.fireEvent('remove', this, e);
16967         
16968         this.validate();
16969         
16970     },
16971     
16972     syncValue : function()
16973     {
16974         if(!this.item.length){
16975             this.clearValue();
16976             return;
16977         }
16978             
16979         var value = [];
16980         var _this = this;
16981         Roo.each(this.item, function(i){
16982             if(_this.valueField){
16983                 value.push(i[_this.valueField]);
16984                 return;
16985             }
16986
16987             value.push(i);
16988         });
16989
16990         this.value = value.join(',');
16991
16992         if(this.hiddenField){
16993             this.hiddenField.dom.value = this.value;
16994         }
16995         
16996         this.store.fireEvent("datachanged", this.store);
16997         
16998         this.validate();
16999     },
17000     
17001     clearItem : function()
17002     {
17003         if(!this.multiple){
17004             return;
17005         }
17006         
17007         this.item = [];
17008         
17009         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
17010            c.remove();
17011         });
17012         
17013         this.syncValue();
17014         
17015         this.validate();
17016         
17017         if(this.tickable && !Roo.isTouch){
17018             this.view.refresh();
17019         }
17020     },
17021     
17022     inputEl: function ()
17023     {
17024         if(Roo.isIOS && this.useNativeIOS){
17025             return this.el.select('select.roo-ios-select', true).first();
17026         }
17027         
17028         if(Roo.isTouch && this.mobileTouchView){
17029             return this.el.select('input.form-control',true).first();
17030         }
17031         
17032         if(this.tickable){
17033             return this.searchField;
17034         }
17035         
17036         return this.el.select('input.form-control',true).first();
17037     },
17038     
17039     onTickableFooterButtonClick : function(e, btn, el)
17040     {
17041         e.preventDefault();
17042         
17043         this.lastItem = Roo.apply([], this.item);
17044         
17045         if(btn && btn.name == 'cancel'){
17046             this.tickItems = Roo.apply([], this.item);
17047             this.collapse();
17048             return;
17049         }
17050         
17051         this.clearItem();
17052         
17053         var _this = this;
17054         
17055         Roo.each(this.tickItems, function(o){
17056             _this.addItem(o);
17057         });
17058         
17059         this.collapse();
17060         
17061     },
17062     
17063     validate : function()
17064     {
17065         if(this.getVisibilityEl().hasClass('hidden')){
17066             return true;
17067         }
17068         
17069         var v = this.getRawValue();
17070         
17071         if(this.multiple){
17072             v = this.getValue();
17073         }
17074         
17075         if(this.disabled || this.allowBlank || v.length){
17076             this.markValid();
17077             return true;
17078         }
17079         
17080         this.markInvalid();
17081         return false;
17082     },
17083     
17084     tickableInputEl : function()
17085     {
17086         if(!this.tickable || !this.editable){
17087             return this.inputEl();
17088         }
17089         
17090         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17091     },
17092     
17093     
17094     getAutoCreateTouchView : function()
17095     {
17096         var id = Roo.id();
17097         
17098         var cfg = {
17099             cls: 'form-group' //input-group
17100         };
17101         
17102         var input =  {
17103             tag: 'input',
17104             id : id,
17105             type : this.inputType,
17106             cls : 'form-control x-combo-noedit',
17107             autocomplete: 'new-password',
17108             placeholder : this.placeholder || '',
17109             readonly : true
17110         };
17111         
17112         if (this.name) {
17113             input.name = this.name;
17114         }
17115         
17116         if (this.size) {
17117             input.cls += ' input-' + this.size;
17118         }
17119         
17120         if (this.disabled) {
17121             input.disabled = true;
17122         }
17123         
17124         var inputblock = {
17125             cls : 'roo-combobox-wrap',
17126             cn : [
17127                 input
17128             ]
17129         };
17130         
17131         if(this.before){
17132             inputblock.cls += ' input-group';
17133             
17134             inputblock.cn.unshift({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-prepend input-group-text',
17137                 html : this.before
17138             });
17139         }
17140         
17141         if(this.removable && !this.multiple){
17142             inputblock.cls += ' roo-removable';
17143             
17144             inputblock.cn.push({
17145                 tag: 'button',
17146                 html : 'x',
17147                 cls : 'roo-combo-removable-btn close'
17148             });
17149         }
17150
17151         if(this.hasFeedback && !this.allowBlank){
17152             
17153             inputblock.cls += ' has-feedback';
17154             
17155             inputblock.cn.push({
17156                 tag: 'span',
17157                 cls: 'glyphicon form-control-feedback'
17158             });
17159             
17160         }
17161         
17162         if (this.after) {
17163             
17164             inputblock.cls += (this.before) ? '' : ' input-group';
17165             
17166             inputblock.cn.push({
17167                 tag :'span',
17168                 cls : 'input-group-addon input-group-append input-group-text',
17169                 html : this.after
17170             });
17171         }
17172
17173         
17174         var ibwrap = inputblock;
17175         
17176         if(this.multiple){
17177             ibwrap = {
17178                 tag: 'ul',
17179                 cls: 'roo-select2-choices',
17180                 cn:[
17181                     {
17182                         tag: 'li',
17183                         cls: 'roo-select2-search-field',
17184                         cn: [
17185
17186                             inputblock
17187                         ]
17188                     }
17189                 ]
17190             };
17191         
17192             
17193         }
17194         
17195         var combobox = {
17196             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17197             cn: [
17198                 {
17199                     tag: 'input',
17200                     type : 'hidden',
17201                     cls: 'form-hidden-field'
17202                 },
17203                 ibwrap
17204             ]
17205         };
17206         
17207         if(!this.multiple && this.showToggleBtn){
17208             
17209             var caret = {
17210                 cls: 'caret'
17211             };
17212             
17213             if (this.caret != false) {
17214                 caret = {
17215                      tag: 'i',
17216                      cls: 'fa fa-' + this.caret
17217                 };
17218                 
17219             }
17220             
17221             combobox.cn.push({
17222                 tag :'span',
17223                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17224                 cn : [
17225                     Roo.bootstrap.version == 3 ? caret : '',
17226                     {
17227                         tag: 'span',
17228                         cls: 'combobox-clear',
17229                         cn  : [
17230                             {
17231                                 tag : 'i',
17232                                 cls: 'icon-remove'
17233                             }
17234                         ]
17235                     }
17236                 ]
17237
17238             })
17239         }
17240         
17241         if(this.multiple){
17242             combobox.cls += ' roo-select2-container-multi';
17243         }
17244         
17245         var align = this.labelAlign || this.parentLabelAlign();
17246         
17247         if (align ==='left' && this.fieldLabel.length) {
17248
17249             cfg.cn = [
17250                 {
17251                    tag : 'i',
17252                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17253                    tooltip : 'This field is required'
17254                 },
17255                 {
17256                     tag: 'label',
17257                     cls : 'control-label col-form-label',
17258                     html : this.fieldLabel
17259
17260                 },
17261                 {
17262                     cls : 'roo-combobox-wrap ', 
17263                     cn: [
17264                         combobox
17265                     ]
17266                 }
17267             ];
17268             
17269             var labelCfg = cfg.cn[1];
17270             var contentCfg = cfg.cn[2];
17271             
17272
17273             if(this.indicatorpos == 'right'){
17274                 cfg.cn = [
17275                     {
17276                         tag: 'label',
17277                         'for' :  id,
17278                         cls : 'control-label col-form-label',
17279                         cn : [
17280                             {
17281                                 tag : 'span',
17282                                 html : this.fieldLabel
17283                             },
17284                             {
17285                                 tag : 'i',
17286                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17287                                 tooltip : 'This field is required'
17288                             }
17289                         ]
17290                     },
17291                     {
17292                         cls : "roo-combobox-wrap ",
17293                         cn: [
17294                             combobox
17295                         ]
17296                     }
17297
17298                 ];
17299                 
17300                 labelCfg = cfg.cn[0];
17301                 contentCfg = cfg.cn[1];
17302             }
17303             
17304            
17305             
17306             if(this.labelWidth > 12){
17307                 labelCfg.style = "width: " + this.labelWidth + 'px';
17308             }
17309            
17310             if(this.labelWidth < 13 && this.labelmd == 0){
17311                 this.labelmd = this.labelWidth;
17312             }
17313             
17314             if(this.labellg > 0){
17315                 labelCfg.cls += ' col-lg-' + this.labellg;
17316                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17317             }
17318             
17319             if(this.labelmd > 0){
17320                 labelCfg.cls += ' col-md-' + this.labelmd;
17321                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17322             }
17323             
17324             if(this.labelsm > 0){
17325                 labelCfg.cls += ' col-sm-' + this.labelsm;
17326                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17327             }
17328             
17329             if(this.labelxs > 0){
17330                 labelCfg.cls += ' col-xs-' + this.labelxs;
17331                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17332             }
17333                 
17334                 
17335         } else if ( this.fieldLabel.length) {
17336             cfg.cn = [
17337                 {
17338                    tag : 'i',
17339                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17340                    tooltip : 'This field is required'
17341                 },
17342                 {
17343                     tag: 'label',
17344                     cls : 'control-label',
17345                     html : this.fieldLabel
17346
17347                 },
17348                 {
17349                     cls : '', 
17350                     cn: [
17351                         combobox
17352                     ]
17353                 }
17354             ];
17355             
17356             if(this.indicatorpos == 'right'){
17357                 cfg.cn = [
17358                     {
17359                         tag: 'label',
17360                         cls : 'control-label',
17361                         html : this.fieldLabel,
17362                         cn : [
17363                             {
17364                                tag : 'i',
17365                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17366                                tooltip : 'This field is required'
17367                             }
17368                         ]
17369                     },
17370                     {
17371                         cls : '', 
17372                         cn: [
17373                             combobox
17374                         ]
17375                     }
17376                 ];
17377             }
17378         } else {
17379             cfg.cn = combobox;    
17380         }
17381         
17382         
17383         var settings = this;
17384         
17385         ['xs','sm','md','lg'].map(function(size){
17386             if (settings[size]) {
17387                 cfg.cls += ' col-' + size + '-' + settings[size];
17388             }
17389         });
17390         
17391         return cfg;
17392     },
17393     
17394     initTouchView : function()
17395     {
17396         this.renderTouchView();
17397         
17398         this.touchViewEl.on('scroll', function(){
17399             this.el.dom.scrollTop = 0;
17400         }, this);
17401         
17402         this.originalValue = this.getValue();
17403         
17404         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17405         
17406         this.inputEl().on("click", this.showTouchView, this);
17407         if (this.triggerEl) {
17408             this.triggerEl.on("click", this.showTouchView, this);
17409         }
17410         
17411         
17412         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17413         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17414         
17415         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17416         
17417         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17418         this.store.on('load', this.onTouchViewLoad, this);
17419         this.store.on('loadexception', this.onTouchViewLoadException, this);
17420         
17421         if(this.hiddenName){
17422             
17423             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17424             
17425             this.hiddenField.dom.value =
17426                 this.hiddenValue !== undefined ? this.hiddenValue :
17427                 this.value !== undefined ? this.value : '';
17428         
17429             this.el.dom.removeAttribute('name');
17430             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17431         }
17432         
17433         if(this.multiple){
17434             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17435             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17436         }
17437         
17438         if(this.removable && !this.multiple){
17439             var close = this.closeTriggerEl();
17440             if(close){
17441                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17442                 close.on('click', this.removeBtnClick, this, close);
17443             }
17444         }
17445         /*
17446          * fix the bug in Safari iOS8
17447          */
17448         this.inputEl().on("focus", function(e){
17449             document.activeElement.blur();
17450         }, this);
17451         
17452         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17453         
17454         return;
17455         
17456         
17457     },
17458     
17459     renderTouchView : function()
17460     {
17461         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17462         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17463         
17464         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17465         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17466         
17467         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17468         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17469         this.touchViewBodyEl.setStyle('overflow', 'auto');
17470         
17471         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17472         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17473         
17474         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17475         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17476         
17477     },
17478     
17479     showTouchView : function()
17480     {
17481         if(this.disabled){
17482             return;
17483         }
17484         
17485         this.touchViewHeaderEl.hide();
17486
17487         if(this.modalTitle.length){
17488             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17489             this.touchViewHeaderEl.show();
17490         }
17491
17492         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17493         this.touchViewEl.show();
17494
17495         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17496         
17497         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17498         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17499
17500         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17501
17502         if(this.modalTitle.length){
17503             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17504         }
17505         
17506         this.touchViewBodyEl.setHeight(bodyHeight);
17507
17508         if(this.animate){
17509             var _this = this;
17510             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17511         }else{
17512             this.touchViewEl.addClass(['in','show']);
17513         }
17514         
17515         if(this._touchViewMask){
17516             Roo.get(document.body).addClass("x-body-masked");
17517             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17518             this._touchViewMask.setStyle('z-index', 10000);
17519             this._touchViewMask.addClass('show');
17520         }
17521         
17522         this.doTouchViewQuery();
17523         
17524     },
17525     
17526     hideTouchView : function()
17527     {
17528         this.touchViewEl.removeClass(['in','show']);
17529
17530         if(this.animate){
17531             var _this = this;
17532             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17533         }else{
17534             this.touchViewEl.setStyle('display', 'none');
17535         }
17536         
17537         if(this._touchViewMask){
17538             this._touchViewMask.removeClass('show');
17539             Roo.get(document.body).removeClass("x-body-masked");
17540         }
17541     },
17542     
17543     setTouchViewValue : function()
17544     {
17545         if(this.multiple){
17546             this.clearItem();
17547         
17548             var _this = this;
17549
17550             Roo.each(this.tickItems, function(o){
17551                 this.addItem(o);
17552             }, this);
17553         }
17554         
17555         this.hideTouchView();
17556     },
17557     
17558     doTouchViewQuery : function()
17559     {
17560         var qe = {
17561             query: '',
17562             forceAll: true,
17563             combo: this,
17564             cancel:false
17565         };
17566         
17567         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17568             return false;
17569         }
17570         
17571         if(!this.alwaysQuery || this.mode == 'local'){
17572             this.onTouchViewLoad();
17573             return;
17574         }
17575         
17576         this.store.load();
17577     },
17578     
17579     onTouchViewBeforeLoad : function(combo,opts)
17580     {
17581         return;
17582     },
17583
17584     // private
17585     onTouchViewLoad : function()
17586     {
17587         if(this.store.getCount() < 1){
17588             this.onTouchViewEmptyResults();
17589             return;
17590         }
17591         
17592         this.clearTouchView();
17593         
17594         var rawValue = this.getRawValue();
17595         
17596         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17597         
17598         this.tickItems = [];
17599         
17600         this.store.data.each(function(d, rowIndex){
17601             var row = this.touchViewListGroup.createChild(template);
17602             
17603             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17604                 row.addClass(d.data.cls);
17605             }
17606             
17607             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17608                 var cfg = {
17609                     data : d.data,
17610                     html : d.data[this.displayField]
17611                 };
17612                 
17613                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17614                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17615                 }
17616             }
17617             row.removeClass('selected');
17618             if(!this.multiple && this.valueField &&
17619                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17620             {
17621                 // radio buttons..
17622                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17623                 row.addClass('selected');
17624             }
17625             
17626             if(this.multiple && this.valueField &&
17627                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17628             {
17629                 
17630                 // checkboxes...
17631                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17632                 this.tickItems.push(d.data);
17633             }
17634             
17635             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17636             
17637         }, this);
17638         
17639         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17640         
17641         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17642
17643         if(this.modalTitle.length){
17644             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17645         }
17646
17647         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17648         
17649         if(this.mobile_restrict_height && listHeight < bodyHeight){
17650             this.touchViewBodyEl.setHeight(listHeight);
17651         }
17652         
17653         var _this = this;
17654         
17655         if(firstChecked && listHeight > bodyHeight){
17656             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17657         }
17658         
17659     },
17660     
17661     onTouchViewLoadException : function()
17662     {
17663         this.hideTouchView();
17664     },
17665     
17666     onTouchViewEmptyResults : function()
17667     {
17668         this.clearTouchView();
17669         
17670         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17671         
17672         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17673         
17674     },
17675     
17676     clearTouchView : function()
17677     {
17678         this.touchViewListGroup.dom.innerHTML = '';
17679     },
17680     
17681     onTouchViewClick : function(e, el, o)
17682     {
17683         e.preventDefault();
17684         
17685         var row = o.row;
17686         var rowIndex = o.rowIndex;
17687         
17688         var r = this.store.getAt(rowIndex);
17689         
17690         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17691             
17692             if(!this.multiple){
17693                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17694                     c.dom.removeAttribute('checked');
17695                 }, this);
17696
17697                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17698
17699                 this.setFromData(r.data);
17700
17701                 var close = this.closeTriggerEl();
17702
17703                 if(close){
17704                     close.show();
17705                 }
17706
17707                 this.hideTouchView();
17708
17709                 this.fireEvent('select', this, r, rowIndex);
17710
17711                 return;
17712             }
17713
17714             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17715                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17716                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17717                 return;
17718             }
17719
17720             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17721             this.addItem(r.data);
17722             this.tickItems.push(r.data);
17723         }
17724     },
17725     
17726     getAutoCreateNativeIOS : function()
17727     {
17728         var cfg = {
17729             cls: 'form-group' //input-group,
17730         };
17731         
17732         var combobox =  {
17733             tag: 'select',
17734             cls : 'roo-ios-select'
17735         };
17736         
17737         if (this.name) {
17738             combobox.name = this.name;
17739         }
17740         
17741         if (this.disabled) {
17742             combobox.disabled = true;
17743         }
17744         
17745         var settings = this;
17746         
17747         ['xs','sm','md','lg'].map(function(size){
17748             if (settings[size]) {
17749                 cfg.cls += ' col-' + size + '-' + settings[size];
17750             }
17751         });
17752         
17753         cfg.cn = combobox;
17754         
17755         return cfg;
17756         
17757     },
17758     
17759     initIOSView : function()
17760     {
17761         this.store.on('load', this.onIOSViewLoad, this);
17762         
17763         return;
17764     },
17765     
17766     onIOSViewLoad : function()
17767     {
17768         if(this.store.getCount() < 1){
17769             return;
17770         }
17771         
17772         this.clearIOSView();
17773         
17774         if(this.allowBlank) {
17775             
17776             var default_text = '-- SELECT --';
17777             
17778             if(this.placeholder.length){
17779                 default_text = this.placeholder;
17780             }
17781             
17782             if(this.emptyTitle.length){
17783                 default_text += ' - ' + this.emptyTitle + ' -';
17784             }
17785             
17786             var opt = this.inputEl().createChild({
17787                 tag: 'option',
17788                 value : 0,
17789                 html : default_text
17790             });
17791             
17792             var o = {};
17793             o[this.valueField] = 0;
17794             o[this.displayField] = default_text;
17795             
17796             this.ios_options.push({
17797                 data : o,
17798                 el : opt
17799             });
17800             
17801         }
17802         
17803         this.store.data.each(function(d, rowIndex){
17804             
17805             var html = '';
17806             
17807             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17808                 html = d.data[this.displayField];
17809             }
17810             
17811             var value = '';
17812             
17813             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17814                 value = d.data[this.valueField];
17815             }
17816             
17817             var option = {
17818                 tag: 'option',
17819                 value : value,
17820                 html : html
17821             };
17822             
17823             if(this.value == d.data[this.valueField]){
17824                 option['selected'] = true;
17825             }
17826             
17827             var opt = this.inputEl().createChild(option);
17828             
17829             this.ios_options.push({
17830                 data : d.data,
17831                 el : opt
17832             });
17833             
17834         }, this);
17835         
17836         this.inputEl().on('change', function(){
17837            this.fireEvent('select', this);
17838         }, this);
17839         
17840     },
17841     
17842     clearIOSView: function()
17843     {
17844         this.inputEl().dom.innerHTML = '';
17845         
17846         this.ios_options = [];
17847     },
17848     
17849     setIOSValue: function(v)
17850     {
17851         this.value = v;
17852         
17853         if(!this.ios_options){
17854             return;
17855         }
17856         
17857         Roo.each(this.ios_options, function(opts){
17858            
17859            opts.el.dom.removeAttribute('selected');
17860            
17861            if(opts.data[this.valueField] != v){
17862                return;
17863            }
17864            
17865            opts.el.dom.setAttribute('selected', true);
17866            
17867         }, this);
17868     }
17869
17870     /** 
17871     * @cfg {Boolean} grow 
17872     * @hide 
17873     */
17874     /** 
17875     * @cfg {Number} growMin 
17876     * @hide 
17877     */
17878     /** 
17879     * @cfg {Number} growMax 
17880     * @hide 
17881     */
17882     /**
17883      * @hide
17884      * @method autoSize
17885      */
17886 });
17887
17888 Roo.apply(Roo.bootstrap.ComboBox,  {
17889     
17890     header : {
17891         tag: 'div',
17892         cls: 'modal-header',
17893         cn: [
17894             {
17895                 tag: 'h4',
17896                 cls: 'modal-title'
17897             }
17898         ]
17899     },
17900     
17901     body : {
17902         tag: 'div',
17903         cls: 'modal-body',
17904         cn: [
17905             {
17906                 tag: 'ul',
17907                 cls: 'list-group'
17908             }
17909         ]
17910     },
17911     
17912     listItemRadio : {
17913         tag: 'li',
17914         cls: 'list-group-item',
17915         cn: [
17916             {
17917                 tag: 'span',
17918                 cls: 'roo-combobox-list-group-item-value'
17919             },
17920             {
17921                 tag: 'div',
17922                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17923                 cn: [
17924                     {
17925                         tag: 'input',
17926                         type: 'radio'
17927                     },
17928                     {
17929                         tag: 'label'
17930                     }
17931                 ]
17932             }
17933         ]
17934     },
17935     
17936     listItemCheckbox : {
17937         tag: 'li',
17938         cls: 'list-group-item',
17939         cn: [
17940             {
17941                 tag: 'span',
17942                 cls: 'roo-combobox-list-group-item-value'
17943             },
17944             {
17945                 tag: 'div',
17946                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17947                 cn: [
17948                     {
17949                         tag: 'input',
17950                         type: 'checkbox'
17951                     },
17952                     {
17953                         tag: 'label'
17954                     }
17955                 ]
17956             }
17957         ]
17958     },
17959     
17960     emptyResult : {
17961         tag: 'div',
17962         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17963     },
17964     
17965     footer : {
17966         tag: 'div',
17967         cls: 'modal-footer',
17968         cn: [
17969             {
17970                 tag: 'div',
17971                 cls: 'row',
17972                 cn: [
17973                     {
17974                         tag: 'div',
17975                         cls: 'col-xs-6 text-left',
17976                         cn: {
17977                             tag: 'button',
17978                             cls: 'btn btn-danger roo-touch-view-cancel',
17979                             html: 'Cancel'
17980                         }
17981                     },
17982                     {
17983                         tag: 'div',
17984                         cls: 'col-xs-6 text-right',
17985                         cn: {
17986                             tag: 'button',
17987                             cls: 'btn btn-success roo-touch-view-ok',
17988                             html: 'OK'
17989                         }
17990                     }
17991                 ]
17992             }
17993         ]
17994         
17995     }
17996 });
17997
17998 Roo.apply(Roo.bootstrap.ComboBox,  {
17999     
18000     touchViewTemplate : {
18001         tag: 'div',
18002         cls: 'modal fade roo-combobox-touch-view',
18003         cn: [
18004             {
18005                 tag: 'div',
18006                 cls: 'modal-dialog',
18007                 style : 'position:fixed', // we have to fix position....
18008                 cn: [
18009                     {
18010                         tag: 'div',
18011                         cls: 'modal-content',
18012                         cn: [
18013                             Roo.bootstrap.ComboBox.header,
18014                             Roo.bootstrap.ComboBox.body,
18015                             Roo.bootstrap.ComboBox.footer
18016                         ]
18017                     }
18018                 ]
18019             }
18020         ]
18021     }
18022 });/*
18023  * Based on:
18024  * Ext JS Library 1.1.1
18025  * Copyright(c) 2006-2007, Ext JS, LLC.
18026  *
18027  * Originally Released Under LGPL - original licence link has changed is not relivant.
18028  *
18029  * Fork - LGPL
18030  * <script type="text/javascript">
18031  */
18032
18033 /**
18034  * @class Roo.View
18035  * @extends Roo.util.Observable
18036  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18037  * This class also supports single and multi selection modes. <br>
18038  * Create a data model bound view:
18039  <pre><code>
18040  var store = new Roo.data.Store(...);
18041
18042  var view = new Roo.View({
18043     el : "my-element",
18044     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18045  
18046     singleSelect: true,
18047     selectedClass: "ydataview-selected",
18048     store: store
18049  });
18050
18051  // listen for node click?
18052  view.on("click", function(vw, index, node, e){
18053  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18054  });
18055
18056  // load XML data
18057  dataModel.load("foobar.xml");
18058  </code></pre>
18059  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18060  * <br><br>
18061  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18062  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18063  * 
18064  * Note: old style constructor is still suported (container, template, config)
18065  * 
18066  * @constructor
18067  * Create a new View
18068  * @param {Object} config The config object
18069  * 
18070  */
18071 Roo.View = function(config, depreciated_tpl, depreciated_config){
18072     
18073     this.parent = false;
18074     
18075     if (typeof(depreciated_tpl) == 'undefined') {
18076         // new way.. - universal constructor.
18077         Roo.apply(this, config);
18078         this.el  = Roo.get(this.el);
18079     } else {
18080         // old format..
18081         this.el  = Roo.get(config);
18082         this.tpl = depreciated_tpl;
18083         Roo.apply(this, depreciated_config);
18084     }
18085     this.wrapEl  = this.el.wrap().wrap();
18086     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18087     
18088     
18089     if(typeof(this.tpl) == "string"){
18090         this.tpl = new Roo.Template(this.tpl);
18091     } else {
18092         // support xtype ctors..
18093         this.tpl = new Roo.factory(this.tpl, Roo);
18094     }
18095     
18096     
18097     this.tpl.compile();
18098     
18099     /** @private */
18100     this.addEvents({
18101         /**
18102          * @event beforeclick
18103          * Fires before a click is processed. Returns false to cancel the default action.
18104          * @param {Roo.View} this
18105          * @param {Number} index The index of the target node
18106          * @param {HTMLElement} node The target node
18107          * @param {Roo.EventObject} e The raw event object
18108          */
18109             "beforeclick" : true,
18110         /**
18111          * @event click
18112          * Fires when a template node is clicked.
18113          * @param {Roo.View} this
18114          * @param {Number} index The index of the target node
18115          * @param {HTMLElement} node The target node
18116          * @param {Roo.EventObject} e The raw event object
18117          */
18118             "click" : true,
18119         /**
18120          * @event dblclick
18121          * Fires when a template node is double clicked.
18122          * @param {Roo.View} this
18123          * @param {Number} index The index of the target node
18124          * @param {HTMLElement} node The target node
18125          * @param {Roo.EventObject} e The raw event object
18126          */
18127             "dblclick" : true,
18128         /**
18129          * @event contextmenu
18130          * Fires when a template node is right clicked.
18131          * @param {Roo.View} this
18132          * @param {Number} index The index of the target node
18133          * @param {HTMLElement} node The target node
18134          * @param {Roo.EventObject} e The raw event object
18135          */
18136             "contextmenu" : true,
18137         /**
18138          * @event selectionchange
18139          * Fires when the selected nodes change.
18140          * @param {Roo.View} this
18141          * @param {Array} selections Array of the selected nodes
18142          */
18143             "selectionchange" : true,
18144     
18145         /**
18146          * @event beforeselect
18147          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18148          * @param {Roo.View} this
18149          * @param {HTMLElement} node The node to be selected
18150          * @param {Array} selections Array of currently selected nodes
18151          */
18152             "beforeselect" : true,
18153         /**
18154          * @event preparedata
18155          * Fires on every row to render, to allow you to change the data.
18156          * @param {Roo.View} this
18157          * @param {Object} data to be rendered (change this)
18158          */
18159           "preparedata" : true
18160           
18161           
18162         });
18163
18164
18165
18166     this.el.on({
18167         "click": this.onClick,
18168         "dblclick": this.onDblClick,
18169         "contextmenu": this.onContextMenu,
18170         scope:this
18171     });
18172
18173     this.selections = [];
18174     this.nodes = [];
18175     this.cmp = new Roo.CompositeElementLite([]);
18176     if(this.store){
18177         this.store = Roo.factory(this.store, Roo.data);
18178         this.setStore(this.store, true);
18179     }
18180     
18181     if ( this.footer && this.footer.xtype) {
18182            
18183          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18184         
18185         this.footer.dataSource = this.store;
18186         this.footer.container = fctr;
18187         this.footer = Roo.factory(this.footer, Roo);
18188         fctr.insertFirst(this.el);
18189         
18190         // this is a bit insane - as the paging toolbar seems to detach the el..
18191 //        dom.parentNode.parentNode.parentNode
18192          // they get detached?
18193     }
18194     
18195     
18196     Roo.View.superclass.constructor.call(this);
18197     
18198     
18199 };
18200
18201 Roo.extend(Roo.View, Roo.util.Observable, {
18202     
18203      /**
18204      * @cfg {Roo.data.Store} store Data store to load data from.
18205      */
18206     store : false,
18207     
18208     /**
18209      * @cfg {String|Roo.Element} el The container element.
18210      */
18211     el : '',
18212     
18213     /**
18214      * @cfg {String|Roo.Template} tpl The template used by this View 
18215      */
18216     tpl : false,
18217     /**
18218      * @cfg {String} dataName the named area of the template to use as the data area
18219      *                          Works with domtemplates roo-name="name"
18220      */
18221     dataName: false,
18222     /**
18223      * @cfg {String} selectedClass The css class to add to selected nodes
18224      */
18225     selectedClass : "x-view-selected",
18226      /**
18227      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18228      */
18229     emptyText : "",
18230     
18231     /**
18232      * @cfg {String} text to display on mask (default Loading)
18233      */
18234     mask : false,
18235     /**
18236      * @cfg {Boolean} multiSelect Allow multiple selection
18237      */
18238     multiSelect : false,
18239     /**
18240      * @cfg {Boolean} singleSelect Allow single selection
18241      */
18242     singleSelect:  false,
18243     
18244     /**
18245      * @cfg {Boolean} toggleSelect - selecting 
18246      */
18247     toggleSelect : false,
18248     
18249     /**
18250      * @cfg {Boolean} tickable - selecting 
18251      */
18252     tickable : false,
18253     
18254     /**
18255      * Returns the element this view is bound to.
18256      * @return {Roo.Element}
18257      */
18258     getEl : function(){
18259         return this.wrapEl;
18260     },
18261     
18262     
18263
18264     /**
18265      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18266      */
18267     refresh : function(){
18268         //Roo.log('refresh');
18269         var t = this.tpl;
18270         
18271         // if we are using something like 'domtemplate', then
18272         // the what gets used is:
18273         // t.applySubtemplate(NAME, data, wrapping data..)
18274         // the outer template then get' applied with
18275         //     the store 'extra data'
18276         // and the body get's added to the
18277         //      roo-name="data" node?
18278         //      <span class='roo-tpl-{name}'></span> ?????
18279         
18280         
18281         
18282         this.clearSelections();
18283         this.el.update("");
18284         var html = [];
18285         var records = this.store.getRange();
18286         if(records.length < 1) {
18287             
18288             // is this valid??  = should it render a template??
18289             
18290             this.el.update(this.emptyText);
18291             return;
18292         }
18293         var el = this.el;
18294         if (this.dataName) {
18295             this.el.update(t.apply(this.store.meta)); //????
18296             el = this.el.child('.roo-tpl-' + this.dataName);
18297         }
18298         
18299         for(var i = 0, len = records.length; i < len; i++){
18300             var data = this.prepareData(records[i].data, i, records[i]);
18301             this.fireEvent("preparedata", this, data, i, records[i]);
18302             
18303             var d = Roo.apply({}, data);
18304             
18305             if(this.tickable){
18306                 Roo.apply(d, {'roo-id' : Roo.id()});
18307                 
18308                 var _this = this;
18309             
18310                 Roo.each(this.parent.item, function(item){
18311                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18312                         return;
18313                     }
18314                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18315                 });
18316             }
18317             
18318             html[html.length] = Roo.util.Format.trim(
18319                 this.dataName ?
18320                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18321                     t.apply(d)
18322             );
18323         }
18324         
18325         
18326         
18327         el.update(html.join(""));
18328         this.nodes = el.dom.childNodes;
18329         this.updateIndexes(0);
18330     },
18331     
18332
18333     /**
18334      * Function to override to reformat the data that is sent to
18335      * the template for each node.
18336      * DEPRICATED - use the preparedata event handler.
18337      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18338      * a JSON object for an UpdateManager bound view).
18339      */
18340     prepareData : function(data, index, record)
18341     {
18342         this.fireEvent("preparedata", this, data, index, record);
18343         return data;
18344     },
18345
18346     onUpdate : function(ds, record){
18347         // Roo.log('on update');   
18348         this.clearSelections();
18349         var index = this.store.indexOf(record);
18350         var n = this.nodes[index];
18351         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18352         n.parentNode.removeChild(n);
18353         this.updateIndexes(index, index);
18354     },
18355
18356     
18357     
18358 // --------- FIXME     
18359     onAdd : function(ds, records, index)
18360     {
18361         //Roo.log(['on Add', ds, records, index] );        
18362         this.clearSelections();
18363         if(this.nodes.length == 0){
18364             this.refresh();
18365             return;
18366         }
18367         var n = this.nodes[index];
18368         for(var i = 0, len = records.length; i < len; i++){
18369             var d = this.prepareData(records[i].data, i, records[i]);
18370             if(n){
18371                 this.tpl.insertBefore(n, d);
18372             }else{
18373                 
18374                 this.tpl.append(this.el, d);
18375             }
18376         }
18377         this.updateIndexes(index);
18378     },
18379
18380     onRemove : function(ds, record, index){
18381        // Roo.log('onRemove');
18382         this.clearSelections();
18383         var el = this.dataName  ?
18384             this.el.child('.roo-tpl-' + this.dataName) :
18385             this.el; 
18386         
18387         el.dom.removeChild(this.nodes[index]);
18388         this.updateIndexes(index);
18389     },
18390
18391     /**
18392      * Refresh an individual node.
18393      * @param {Number} index
18394      */
18395     refreshNode : function(index){
18396         this.onUpdate(this.store, this.store.getAt(index));
18397     },
18398
18399     updateIndexes : function(startIndex, endIndex){
18400         var ns = this.nodes;
18401         startIndex = startIndex || 0;
18402         endIndex = endIndex || ns.length - 1;
18403         for(var i = startIndex; i <= endIndex; i++){
18404             ns[i].nodeIndex = i;
18405         }
18406     },
18407
18408     /**
18409      * Changes the data store this view uses and refresh the view.
18410      * @param {Store} store
18411      */
18412     setStore : function(store, initial){
18413         if(!initial && this.store){
18414             this.store.un("datachanged", this.refresh);
18415             this.store.un("add", this.onAdd);
18416             this.store.un("remove", this.onRemove);
18417             this.store.un("update", this.onUpdate);
18418             this.store.un("clear", this.refresh);
18419             this.store.un("beforeload", this.onBeforeLoad);
18420             this.store.un("load", this.onLoad);
18421             this.store.un("loadexception", this.onLoad);
18422         }
18423         if(store){
18424           
18425             store.on("datachanged", this.refresh, this);
18426             store.on("add", this.onAdd, this);
18427             store.on("remove", this.onRemove, this);
18428             store.on("update", this.onUpdate, this);
18429             store.on("clear", this.refresh, this);
18430             store.on("beforeload", this.onBeforeLoad, this);
18431             store.on("load", this.onLoad, this);
18432             store.on("loadexception", this.onLoad, this);
18433         }
18434         
18435         if(store){
18436             this.refresh();
18437         }
18438     },
18439     /**
18440      * onbeforeLoad - masks the loading area.
18441      *
18442      */
18443     onBeforeLoad : function(store,opts)
18444     {
18445          //Roo.log('onBeforeLoad');   
18446         if (!opts.add) {
18447             this.el.update("");
18448         }
18449         this.el.mask(this.mask ? this.mask : "Loading" ); 
18450     },
18451     onLoad : function ()
18452     {
18453         this.el.unmask();
18454     },
18455     
18456
18457     /**
18458      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18459      * @param {HTMLElement} node
18460      * @return {HTMLElement} The template node
18461      */
18462     findItemFromChild : function(node){
18463         var el = this.dataName  ?
18464             this.el.child('.roo-tpl-' + this.dataName,true) :
18465             this.el.dom; 
18466         
18467         if(!node || node.parentNode == el){
18468                     return node;
18469             }
18470             var p = node.parentNode;
18471             while(p && p != el){
18472             if(p.parentNode == el){
18473                 return p;
18474             }
18475             p = p.parentNode;
18476         }
18477             return null;
18478     },
18479
18480     /** @ignore */
18481     onClick : function(e){
18482         var item = this.findItemFromChild(e.getTarget());
18483         if(item){
18484             var index = this.indexOf(item);
18485             if(this.onItemClick(item, index, e) !== false){
18486                 this.fireEvent("click", this, index, item, e);
18487             }
18488         }else{
18489             this.clearSelections();
18490         }
18491     },
18492
18493     /** @ignore */
18494     onContextMenu : function(e){
18495         var item = this.findItemFromChild(e.getTarget());
18496         if(item){
18497             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18498         }
18499     },
18500
18501     /** @ignore */
18502     onDblClick : function(e){
18503         var item = this.findItemFromChild(e.getTarget());
18504         if(item){
18505             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18506         }
18507     },
18508
18509     onItemClick : function(item, index, e)
18510     {
18511         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18512             return false;
18513         }
18514         if (this.toggleSelect) {
18515             var m = this.isSelected(item) ? 'unselect' : 'select';
18516             //Roo.log(m);
18517             var _t = this;
18518             _t[m](item, true, false);
18519             return true;
18520         }
18521         if(this.multiSelect || this.singleSelect){
18522             if(this.multiSelect && e.shiftKey && this.lastSelection){
18523                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18524             }else{
18525                 this.select(item, this.multiSelect && e.ctrlKey);
18526                 this.lastSelection = item;
18527             }
18528             
18529             if(!this.tickable){
18530                 e.preventDefault();
18531             }
18532             
18533         }
18534         return true;
18535     },
18536
18537     /**
18538      * Get the number of selected nodes.
18539      * @return {Number}
18540      */
18541     getSelectionCount : function(){
18542         return this.selections.length;
18543     },
18544
18545     /**
18546      * Get the currently selected nodes.
18547      * @return {Array} An array of HTMLElements
18548      */
18549     getSelectedNodes : function(){
18550         return this.selections;
18551     },
18552
18553     /**
18554      * Get the indexes of the selected nodes.
18555      * @return {Array}
18556      */
18557     getSelectedIndexes : function(){
18558         var indexes = [], s = this.selections;
18559         for(var i = 0, len = s.length; i < len; i++){
18560             indexes.push(s[i].nodeIndex);
18561         }
18562         return indexes;
18563     },
18564
18565     /**
18566      * Clear all selections
18567      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18568      */
18569     clearSelections : function(suppressEvent){
18570         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18571             this.cmp.elements = this.selections;
18572             this.cmp.removeClass(this.selectedClass);
18573             this.selections = [];
18574             if(!suppressEvent){
18575                 this.fireEvent("selectionchange", this, this.selections);
18576             }
18577         }
18578     },
18579
18580     /**
18581      * Returns true if the passed node is selected
18582      * @param {HTMLElement/Number} node The node or node index
18583      * @return {Boolean}
18584      */
18585     isSelected : function(node){
18586         var s = this.selections;
18587         if(s.length < 1){
18588             return false;
18589         }
18590         node = this.getNode(node);
18591         return s.indexOf(node) !== -1;
18592     },
18593
18594     /**
18595      * Selects nodes.
18596      * @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
18597      * @param {Boolean} keepExisting (optional) true to keep existing selections
18598      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18599      */
18600     select : function(nodeInfo, keepExisting, suppressEvent){
18601         if(nodeInfo instanceof Array){
18602             if(!keepExisting){
18603                 this.clearSelections(true);
18604             }
18605             for(var i = 0, len = nodeInfo.length; i < len; i++){
18606                 this.select(nodeInfo[i], true, true);
18607             }
18608             return;
18609         } 
18610         var node = this.getNode(nodeInfo);
18611         if(!node || this.isSelected(node)){
18612             return; // already selected.
18613         }
18614         if(!keepExisting){
18615             this.clearSelections(true);
18616         }
18617         
18618         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18619             Roo.fly(node).addClass(this.selectedClass);
18620             this.selections.push(node);
18621             if(!suppressEvent){
18622                 this.fireEvent("selectionchange", this, this.selections);
18623             }
18624         }
18625         
18626         
18627     },
18628       /**
18629      * Unselects nodes.
18630      * @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
18631      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18632      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18633      */
18634     unselect : function(nodeInfo, keepExisting, suppressEvent)
18635     {
18636         if(nodeInfo instanceof Array){
18637             Roo.each(this.selections, function(s) {
18638                 this.unselect(s, nodeInfo);
18639             }, this);
18640             return;
18641         }
18642         var node = this.getNode(nodeInfo);
18643         if(!node || !this.isSelected(node)){
18644             //Roo.log("not selected");
18645             return; // not selected.
18646         }
18647         // fireevent???
18648         var ns = [];
18649         Roo.each(this.selections, function(s) {
18650             if (s == node ) {
18651                 Roo.fly(node).removeClass(this.selectedClass);
18652
18653                 return;
18654             }
18655             ns.push(s);
18656         },this);
18657         
18658         this.selections= ns;
18659         this.fireEvent("selectionchange", this, this.selections);
18660     },
18661
18662     /**
18663      * Gets a template node.
18664      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18665      * @return {HTMLElement} The node or null if it wasn't found
18666      */
18667     getNode : function(nodeInfo){
18668         if(typeof nodeInfo == "string"){
18669             return document.getElementById(nodeInfo);
18670         }else if(typeof nodeInfo == "number"){
18671             return this.nodes[nodeInfo];
18672         }
18673         return nodeInfo;
18674     },
18675
18676     /**
18677      * Gets a range template nodes.
18678      * @param {Number} startIndex
18679      * @param {Number} endIndex
18680      * @return {Array} An array of nodes
18681      */
18682     getNodes : function(start, end){
18683         var ns = this.nodes;
18684         start = start || 0;
18685         end = typeof end == "undefined" ? ns.length - 1 : end;
18686         var nodes = [];
18687         if(start <= end){
18688             for(var i = start; i <= end; i++){
18689                 nodes.push(ns[i]);
18690             }
18691         } else{
18692             for(var i = start; i >= end; i--){
18693                 nodes.push(ns[i]);
18694             }
18695         }
18696         return nodes;
18697     },
18698
18699     /**
18700      * Finds the index of the passed node
18701      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18702      * @return {Number} The index of the node or -1
18703      */
18704     indexOf : function(node){
18705         node = this.getNode(node);
18706         if(typeof node.nodeIndex == "number"){
18707             return node.nodeIndex;
18708         }
18709         var ns = this.nodes;
18710         for(var i = 0, len = ns.length; i < len; i++){
18711             if(ns[i] == node){
18712                 return i;
18713             }
18714         }
18715         return -1;
18716     }
18717 });
18718 /*
18719  * - LGPL
18720  *
18721  * based on jquery fullcalendar
18722  * 
18723  */
18724
18725 Roo.bootstrap = Roo.bootstrap || {};
18726 /**
18727  * @class Roo.bootstrap.Calendar
18728  * @extends Roo.bootstrap.Component
18729  * Bootstrap Calendar class
18730  * @cfg {Boolean} loadMask (true|false) default false
18731  * @cfg {Object} header generate the user specific header of the calendar, default false
18732
18733  * @constructor
18734  * Create a new Container
18735  * @param {Object} config The config object
18736  */
18737
18738
18739
18740 Roo.bootstrap.Calendar = function(config){
18741     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18742      this.addEvents({
18743         /**
18744              * @event select
18745              * Fires when a date is selected
18746              * @param {DatePicker} this
18747              * @param {Date} date The selected date
18748              */
18749         'select': true,
18750         /**
18751              * @event monthchange
18752              * Fires when the displayed month changes 
18753              * @param {DatePicker} this
18754              * @param {Date} date The selected month
18755              */
18756         'monthchange': true,
18757         /**
18758              * @event evententer
18759              * Fires when mouse over an event
18760              * @param {Calendar} this
18761              * @param {event} Event
18762              */
18763         'evententer': true,
18764         /**
18765              * @event eventleave
18766              * Fires when the mouse leaves an
18767              * @param {Calendar} this
18768              * @param {event}
18769              */
18770         'eventleave': true,
18771         /**
18772              * @event eventclick
18773              * Fires when the mouse click an
18774              * @param {Calendar} this
18775              * @param {event}
18776              */
18777         'eventclick': true
18778         
18779     });
18780
18781 };
18782
18783 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18784     
18785      /**
18786      * @cfg {Number} startDay
18787      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18788      */
18789     startDay : 0,
18790     
18791     loadMask : false,
18792     
18793     header : false,
18794       
18795     getAutoCreate : function(){
18796         
18797         
18798         var fc_button = function(name, corner, style, content ) {
18799             return Roo.apply({},{
18800                 tag : 'span',
18801                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18802                          (corner.length ?
18803                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18804                             ''
18805                         ),
18806                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18807                 unselectable: 'on'
18808             });
18809         };
18810         
18811         var header = {};
18812         
18813         if(!this.header){
18814             header = {
18815                 tag : 'table',
18816                 cls : 'fc-header',
18817                 style : 'width:100%',
18818                 cn : [
18819                     {
18820                         tag: 'tr',
18821                         cn : [
18822                             {
18823                                 tag : 'td',
18824                                 cls : 'fc-header-left',
18825                                 cn : [
18826                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18827                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18828                                     { tag: 'span', cls: 'fc-header-space' },
18829                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18830
18831
18832                                 ]
18833                             },
18834
18835                             {
18836                                 tag : 'td',
18837                                 cls : 'fc-header-center',
18838                                 cn : [
18839                                     {
18840                                         tag: 'span',
18841                                         cls: 'fc-header-title',
18842                                         cn : {
18843                                             tag: 'H2',
18844                                             html : 'month / year'
18845                                         }
18846                                     }
18847
18848                                 ]
18849                             },
18850                             {
18851                                 tag : 'td',
18852                                 cls : 'fc-header-right',
18853                                 cn : [
18854                               /*      fc_button('month', 'left', '', 'month' ),
18855                                     fc_button('week', '', '', 'week' ),
18856                                     fc_button('day', 'right', '', 'day' )
18857                                 */    
18858
18859                                 ]
18860                             }
18861
18862                         ]
18863                     }
18864                 ]
18865             };
18866         }
18867         
18868         header = this.header;
18869         
18870        
18871         var cal_heads = function() {
18872             var ret = [];
18873             // fixme - handle this.
18874             
18875             for (var i =0; i < Date.dayNames.length; i++) {
18876                 var d = Date.dayNames[i];
18877                 ret.push({
18878                     tag: 'th',
18879                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18880                     html : d.substring(0,3)
18881                 });
18882                 
18883             }
18884             ret[0].cls += ' fc-first';
18885             ret[6].cls += ' fc-last';
18886             return ret;
18887         };
18888         var cal_cell = function(n) {
18889             return  {
18890                 tag: 'td',
18891                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18892                 cn : [
18893                     {
18894                         cn : [
18895                             {
18896                                 cls: 'fc-day-number',
18897                                 html: 'D'
18898                             },
18899                             {
18900                                 cls: 'fc-day-content',
18901                              
18902                                 cn : [
18903                                      {
18904                                         style: 'position: relative;' // height: 17px;
18905                                     }
18906                                 ]
18907                             }
18908                             
18909                             
18910                         ]
18911                     }
18912                 ]
18913                 
18914             }
18915         };
18916         var cal_rows = function() {
18917             
18918             var ret = [];
18919             for (var r = 0; r < 6; r++) {
18920                 var row= {
18921                     tag : 'tr',
18922                     cls : 'fc-week',
18923                     cn : []
18924                 };
18925                 
18926                 for (var i =0; i < Date.dayNames.length; i++) {
18927                     var d = Date.dayNames[i];
18928                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18929
18930                 }
18931                 row.cn[0].cls+=' fc-first';
18932                 row.cn[0].cn[0].style = 'min-height:90px';
18933                 row.cn[6].cls+=' fc-last';
18934                 ret.push(row);
18935                 
18936             }
18937             ret[0].cls += ' fc-first';
18938             ret[4].cls += ' fc-prev-last';
18939             ret[5].cls += ' fc-last';
18940             return ret;
18941             
18942         };
18943         
18944         var cal_table = {
18945             tag: 'table',
18946             cls: 'fc-border-separate',
18947             style : 'width:100%',
18948             cellspacing  : 0,
18949             cn : [
18950                 { 
18951                     tag: 'thead',
18952                     cn : [
18953                         { 
18954                             tag: 'tr',
18955                             cls : 'fc-first fc-last',
18956                             cn : cal_heads()
18957                         }
18958                     ]
18959                 },
18960                 { 
18961                     tag: 'tbody',
18962                     cn : cal_rows()
18963                 }
18964                   
18965             ]
18966         };
18967          
18968          var cfg = {
18969             cls : 'fc fc-ltr',
18970             cn : [
18971                 header,
18972                 {
18973                     cls : 'fc-content',
18974                     style : "position: relative;",
18975                     cn : [
18976                         {
18977                             cls : 'fc-view fc-view-month fc-grid',
18978                             style : 'position: relative',
18979                             unselectable : 'on',
18980                             cn : [
18981                                 {
18982                                     cls : 'fc-event-container',
18983                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18984                                 },
18985                                 cal_table
18986                             ]
18987                         }
18988                     ]
18989     
18990                 }
18991            ] 
18992             
18993         };
18994         
18995          
18996         
18997         return cfg;
18998     },
18999     
19000     
19001     initEvents : function()
19002     {
19003         if(!this.store){
19004             throw "can not find store for calendar";
19005         }
19006         
19007         var mark = {
19008             tag: "div",
19009             cls:"x-dlg-mask",
19010             style: "text-align:center",
19011             cn: [
19012                 {
19013                     tag: "div",
19014                     style: "background-color:white;width:50%;margin:250 auto",
19015                     cn: [
19016                         {
19017                             tag: "img",
19018                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
19019                         },
19020                         {
19021                             tag: "span",
19022                             html: "Loading"
19023                         }
19024                         
19025                     ]
19026                 }
19027             ]
19028         };
19029         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
19030         
19031         var size = this.el.select('.fc-content', true).first().getSize();
19032         this.maskEl.setSize(size.width, size.height);
19033         this.maskEl.enableDisplayMode("block");
19034         if(!this.loadMask){
19035             this.maskEl.hide();
19036         }
19037         
19038         this.store = Roo.factory(this.store, Roo.data);
19039         this.store.on('load', this.onLoad, this);
19040         this.store.on('beforeload', this.onBeforeLoad, this);
19041         
19042         this.resize();
19043         
19044         this.cells = this.el.select('.fc-day',true);
19045         //Roo.log(this.cells);
19046         this.textNodes = this.el.query('.fc-day-number');
19047         this.cells.addClassOnOver('fc-state-hover');
19048         
19049         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19050         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19051         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19052         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19053         
19054         this.on('monthchange', this.onMonthChange, this);
19055         
19056         this.update(new Date().clearTime());
19057     },
19058     
19059     resize : function() {
19060         var sz  = this.el.getSize();
19061         
19062         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19063         this.el.select('.fc-day-content div',true).setHeight(34);
19064     },
19065     
19066     
19067     // private
19068     showPrevMonth : function(e){
19069         this.update(this.activeDate.add("mo", -1));
19070     },
19071     showToday : function(e){
19072         this.update(new Date().clearTime());
19073     },
19074     // private
19075     showNextMonth : function(e){
19076         this.update(this.activeDate.add("mo", 1));
19077     },
19078
19079     // private
19080     showPrevYear : function(){
19081         this.update(this.activeDate.add("y", -1));
19082     },
19083
19084     // private
19085     showNextYear : function(){
19086         this.update(this.activeDate.add("y", 1));
19087     },
19088
19089     
19090    // private
19091     update : function(date)
19092     {
19093         var vd = this.activeDate;
19094         this.activeDate = date;
19095 //        if(vd && this.el){
19096 //            var t = date.getTime();
19097 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19098 //                Roo.log('using add remove');
19099 //                
19100 //                this.fireEvent('monthchange', this, date);
19101 //                
19102 //                this.cells.removeClass("fc-state-highlight");
19103 //                this.cells.each(function(c){
19104 //                   if(c.dateValue == t){
19105 //                       c.addClass("fc-state-highlight");
19106 //                       setTimeout(function(){
19107 //                            try{c.dom.firstChild.focus();}catch(e){}
19108 //                       }, 50);
19109 //                       return false;
19110 //                   }
19111 //                   return true;
19112 //                });
19113 //                return;
19114 //            }
19115 //        }
19116         
19117         var days = date.getDaysInMonth();
19118         
19119         var firstOfMonth = date.getFirstDateOfMonth();
19120         var startingPos = firstOfMonth.getDay()-this.startDay;
19121         
19122         if(startingPos < this.startDay){
19123             startingPos += 7;
19124         }
19125         
19126         var pm = date.add(Date.MONTH, -1);
19127         var prevStart = pm.getDaysInMonth()-startingPos;
19128 //        
19129         this.cells = this.el.select('.fc-day',true);
19130         this.textNodes = this.el.query('.fc-day-number');
19131         this.cells.addClassOnOver('fc-state-hover');
19132         
19133         var cells = this.cells.elements;
19134         var textEls = this.textNodes;
19135         
19136         Roo.each(cells, function(cell){
19137             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19138         });
19139         
19140         days += startingPos;
19141
19142         // convert everything to numbers so it's fast
19143         var day = 86400000;
19144         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19145         //Roo.log(d);
19146         //Roo.log(pm);
19147         //Roo.log(prevStart);
19148         
19149         var today = new Date().clearTime().getTime();
19150         var sel = date.clearTime().getTime();
19151         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19152         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19153         var ddMatch = this.disabledDatesRE;
19154         var ddText = this.disabledDatesText;
19155         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19156         var ddaysText = this.disabledDaysText;
19157         var format = this.format;
19158         
19159         var setCellClass = function(cal, cell){
19160             cell.row = 0;
19161             cell.events = [];
19162             cell.more = [];
19163             //Roo.log('set Cell Class');
19164             cell.title = "";
19165             var t = d.getTime();
19166             
19167             //Roo.log(d);
19168             
19169             cell.dateValue = t;
19170             if(t == today){
19171                 cell.className += " fc-today";
19172                 cell.className += " fc-state-highlight";
19173                 cell.title = cal.todayText;
19174             }
19175             if(t == sel){
19176                 // disable highlight in other month..
19177                 //cell.className += " fc-state-highlight";
19178                 
19179             }
19180             // disabling
19181             if(t < min) {
19182                 cell.className = " fc-state-disabled";
19183                 cell.title = cal.minText;
19184                 return;
19185             }
19186             if(t > max) {
19187                 cell.className = " fc-state-disabled";
19188                 cell.title = cal.maxText;
19189                 return;
19190             }
19191             if(ddays){
19192                 if(ddays.indexOf(d.getDay()) != -1){
19193                     cell.title = ddaysText;
19194                     cell.className = " fc-state-disabled";
19195                 }
19196             }
19197             if(ddMatch && format){
19198                 var fvalue = d.dateFormat(format);
19199                 if(ddMatch.test(fvalue)){
19200                     cell.title = ddText.replace("%0", fvalue);
19201                     cell.className = " fc-state-disabled";
19202                 }
19203             }
19204             
19205             if (!cell.initialClassName) {
19206                 cell.initialClassName = cell.dom.className;
19207             }
19208             
19209             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19210         };
19211
19212         var i = 0;
19213         
19214         for(; i < startingPos; i++) {
19215             textEls[i].innerHTML = (++prevStart);
19216             d.setDate(d.getDate()+1);
19217             
19218             cells[i].className = "fc-past fc-other-month";
19219             setCellClass(this, cells[i]);
19220         }
19221         
19222         var intDay = 0;
19223         
19224         for(; i < days; i++){
19225             intDay = i - startingPos + 1;
19226             textEls[i].innerHTML = (intDay);
19227             d.setDate(d.getDate()+1);
19228             
19229             cells[i].className = ''; // "x-date-active";
19230             setCellClass(this, cells[i]);
19231         }
19232         var extraDays = 0;
19233         
19234         for(; i < 42; i++) {
19235             textEls[i].innerHTML = (++extraDays);
19236             d.setDate(d.getDate()+1);
19237             
19238             cells[i].className = "fc-future fc-other-month";
19239             setCellClass(this, cells[i]);
19240         }
19241         
19242         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19243         
19244         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19245         
19246         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19247         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19248         
19249         if(totalRows != 6){
19250             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19251             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19252         }
19253         
19254         this.fireEvent('monthchange', this, date);
19255         
19256         
19257         /*
19258         if(!this.internalRender){
19259             var main = this.el.dom.firstChild;
19260             var w = main.offsetWidth;
19261             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19262             Roo.fly(main).setWidth(w);
19263             this.internalRender = true;
19264             // opera does not respect the auto grow header center column
19265             // then, after it gets a width opera refuses to recalculate
19266             // without a second pass
19267             if(Roo.isOpera && !this.secondPass){
19268                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19269                 this.secondPass = true;
19270                 this.update.defer(10, this, [date]);
19271             }
19272         }
19273         */
19274         
19275     },
19276     
19277     findCell : function(dt) {
19278         dt = dt.clearTime().getTime();
19279         var ret = false;
19280         this.cells.each(function(c){
19281             //Roo.log("check " +c.dateValue + '?=' + dt);
19282             if(c.dateValue == dt){
19283                 ret = c;
19284                 return false;
19285             }
19286             return true;
19287         });
19288         
19289         return ret;
19290     },
19291     
19292     findCells : function(ev) {
19293         var s = ev.start.clone().clearTime().getTime();
19294        // Roo.log(s);
19295         var e= ev.end.clone().clearTime().getTime();
19296        // Roo.log(e);
19297         var ret = [];
19298         this.cells.each(function(c){
19299              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19300             
19301             if(c.dateValue > e){
19302                 return ;
19303             }
19304             if(c.dateValue < s){
19305                 return ;
19306             }
19307             ret.push(c);
19308         });
19309         
19310         return ret;    
19311     },
19312     
19313 //    findBestRow: function(cells)
19314 //    {
19315 //        var ret = 0;
19316 //        
19317 //        for (var i =0 ; i < cells.length;i++) {
19318 //            ret  = Math.max(cells[i].rows || 0,ret);
19319 //        }
19320 //        return ret;
19321 //        
19322 //    },
19323     
19324     
19325     addItem : function(ev)
19326     {
19327         // look for vertical location slot in
19328         var cells = this.findCells(ev);
19329         
19330 //        ev.row = this.findBestRow(cells);
19331         
19332         // work out the location.
19333         
19334         var crow = false;
19335         var rows = [];
19336         for(var i =0; i < cells.length; i++) {
19337             
19338             cells[i].row = cells[0].row;
19339             
19340             if(i == 0){
19341                 cells[i].row = cells[i].row + 1;
19342             }
19343             
19344             if (!crow) {
19345                 crow = {
19346                     start : cells[i],
19347                     end :  cells[i]
19348                 };
19349                 continue;
19350             }
19351             if (crow.start.getY() == cells[i].getY()) {
19352                 // on same row.
19353                 crow.end = cells[i];
19354                 continue;
19355             }
19356             // different row.
19357             rows.push(crow);
19358             crow = {
19359                 start: cells[i],
19360                 end : cells[i]
19361             };
19362             
19363         }
19364         
19365         rows.push(crow);
19366         ev.els = [];
19367         ev.rows = rows;
19368         ev.cells = cells;
19369         
19370         cells[0].events.push(ev);
19371         
19372         this.calevents.push(ev);
19373     },
19374     
19375     clearEvents: function() {
19376         
19377         if(!this.calevents){
19378             return;
19379         }
19380         
19381         Roo.each(this.cells.elements, function(c){
19382             c.row = 0;
19383             c.events = [];
19384             c.more = [];
19385         });
19386         
19387         Roo.each(this.calevents, function(e) {
19388             Roo.each(e.els, function(el) {
19389                 el.un('mouseenter' ,this.onEventEnter, this);
19390                 el.un('mouseleave' ,this.onEventLeave, this);
19391                 el.remove();
19392             },this);
19393         },this);
19394         
19395         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19396             e.remove();
19397         });
19398         
19399     },
19400     
19401     renderEvents: function()
19402     {   
19403         var _this = this;
19404         
19405         this.cells.each(function(c) {
19406             
19407             if(c.row < 5){
19408                 return;
19409             }
19410             
19411             var ev = c.events;
19412             
19413             var r = 4;
19414             if(c.row != c.events.length){
19415                 r = 4 - (4 - (c.row - c.events.length));
19416             }
19417             
19418             c.events = ev.slice(0, r);
19419             c.more = ev.slice(r);
19420             
19421             if(c.more.length && c.more.length == 1){
19422                 c.events.push(c.more.pop());
19423             }
19424             
19425             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19426             
19427         });
19428             
19429         this.cells.each(function(c) {
19430             
19431             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19432             
19433             
19434             for (var e = 0; e < c.events.length; e++){
19435                 var ev = c.events[e];
19436                 var rows = ev.rows;
19437                 
19438                 for(var i = 0; i < rows.length; i++) {
19439                 
19440                     // how many rows should it span..
19441
19442                     var  cfg = {
19443                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19444                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19445
19446                         unselectable : "on",
19447                         cn : [
19448                             {
19449                                 cls: 'fc-event-inner',
19450                                 cn : [
19451     //                                {
19452     //                                  tag:'span',
19453     //                                  cls: 'fc-event-time',
19454     //                                  html : cells.length > 1 ? '' : ev.time
19455     //                                },
19456                                     {
19457                                       tag:'span',
19458                                       cls: 'fc-event-title',
19459                                       html : String.format('{0}', ev.title)
19460                                     }
19461
19462
19463                                 ]
19464                             },
19465                             {
19466                                 cls: 'ui-resizable-handle ui-resizable-e',
19467                                 html : '&nbsp;&nbsp;&nbsp'
19468                             }
19469
19470                         ]
19471                     };
19472
19473                     if (i == 0) {
19474                         cfg.cls += ' fc-event-start';
19475                     }
19476                     if ((i+1) == rows.length) {
19477                         cfg.cls += ' fc-event-end';
19478                     }
19479
19480                     var ctr = _this.el.select('.fc-event-container',true).first();
19481                     var cg = ctr.createChild(cfg);
19482
19483                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19484                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19485
19486                     var r = (c.more.length) ? 1 : 0;
19487                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19488                     cg.setWidth(ebox.right - sbox.x -2);
19489
19490                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19491                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19492                     cg.on('click', _this.onEventClick, _this, ev);
19493
19494                     ev.els.push(cg);
19495                     
19496                 }
19497                 
19498             }
19499             
19500             
19501             if(c.more.length){
19502                 var  cfg = {
19503                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19504                     style : 'position: absolute',
19505                     unselectable : "on",
19506                     cn : [
19507                         {
19508                             cls: 'fc-event-inner',
19509                             cn : [
19510                                 {
19511                                   tag:'span',
19512                                   cls: 'fc-event-title',
19513                                   html : 'More'
19514                                 }
19515
19516
19517                             ]
19518                         },
19519                         {
19520                             cls: 'ui-resizable-handle ui-resizable-e',
19521                             html : '&nbsp;&nbsp;&nbsp'
19522                         }
19523
19524                     ]
19525                 };
19526
19527                 var ctr = _this.el.select('.fc-event-container',true).first();
19528                 var cg = ctr.createChild(cfg);
19529
19530                 var sbox = c.select('.fc-day-content',true).first().getBox();
19531                 var ebox = c.select('.fc-day-content',true).first().getBox();
19532                 //Roo.log(cg);
19533                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19534                 cg.setWidth(ebox.right - sbox.x -2);
19535
19536                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19537                 
19538             }
19539             
19540         });
19541         
19542         
19543         
19544     },
19545     
19546     onEventEnter: function (e, el,event,d) {
19547         this.fireEvent('evententer', this, el, event);
19548     },
19549     
19550     onEventLeave: function (e, el,event,d) {
19551         this.fireEvent('eventleave', this, el, event);
19552     },
19553     
19554     onEventClick: function (e, el,event,d) {
19555         this.fireEvent('eventclick', this, el, event);
19556     },
19557     
19558     onMonthChange: function () {
19559         this.store.load();
19560     },
19561     
19562     onMoreEventClick: function(e, el, more)
19563     {
19564         var _this = this;
19565         
19566         this.calpopover.placement = 'right';
19567         this.calpopover.setTitle('More');
19568         
19569         this.calpopover.setContent('');
19570         
19571         var ctr = this.calpopover.el.select('.popover-content', true).first();
19572         
19573         Roo.each(more, function(m){
19574             var cfg = {
19575                 cls : 'fc-event-hori fc-event-draggable',
19576                 html : m.title
19577             };
19578             var cg = ctr.createChild(cfg);
19579             
19580             cg.on('click', _this.onEventClick, _this, m);
19581         });
19582         
19583         this.calpopover.show(el);
19584         
19585         
19586     },
19587     
19588     onLoad: function () 
19589     {   
19590         this.calevents = [];
19591         var cal = this;
19592         
19593         if(this.store.getCount() > 0){
19594             this.store.data.each(function(d){
19595                cal.addItem({
19596                     id : d.data.id,
19597                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19598                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19599                     time : d.data.start_time,
19600                     title : d.data.title,
19601                     description : d.data.description,
19602                     venue : d.data.venue
19603                 });
19604             });
19605         }
19606         
19607         this.renderEvents();
19608         
19609         if(this.calevents.length && this.loadMask){
19610             this.maskEl.hide();
19611         }
19612     },
19613     
19614     onBeforeLoad: function()
19615     {
19616         this.clearEvents();
19617         if(this.loadMask){
19618             this.maskEl.show();
19619         }
19620     }
19621 });
19622
19623  
19624  /*
19625  * - LGPL
19626  *
19627  * element
19628  * 
19629  */
19630
19631 /**
19632  * @class Roo.bootstrap.Popover
19633  * @extends Roo.bootstrap.Component
19634  * Bootstrap Popover class
19635  * @cfg {String} html contents of the popover   (or false to use children..)
19636  * @cfg {String} title of popover (or false to hide)
19637  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19638  * @cfg {String} trigger click || hover (or false to trigger manually)
19639  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19640  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19641  *      - if false and it has a 'parent' then it will be automatically added to that element
19642  *      - if string - Roo.get  will be called 
19643  * @cfg {Number} delay - delay before showing
19644  
19645  * @constructor
19646  * Create a new Popover
19647  * @param {Object} config The config object
19648  */
19649
19650 Roo.bootstrap.Popover = function(config){
19651     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19652     
19653     this.addEvents({
19654         // raw events
19655          /**
19656          * @event show
19657          * After the popover show
19658          * 
19659          * @param {Roo.bootstrap.Popover} this
19660          */
19661         "show" : true,
19662         /**
19663          * @event hide
19664          * After the popover hide
19665          * 
19666          * @param {Roo.bootstrap.Popover} this
19667          */
19668         "hide" : true
19669     });
19670 };
19671
19672 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19673     
19674     title: false,
19675     html: false,
19676     
19677     placement : 'right',
19678     trigger : 'hover', // hover
19679     modal : false,
19680     delay : 0,
19681     
19682     over: false,
19683     
19684     can_build_overlaid : false,
19685     
19686     maskEl : false, // the mask element
19687     headerEl : false,
19688     contentEl : false,
19689     alignEl : false, // when show is called with an element - this get's stored.
19690     
19691     getChildContainer : function()
19692     {
19693         return this.contentEl;
19694         
19695     },
19696     getPopoverHeader : function()
19697     {
19698         this.title = true; // flag not to hide it..
19699         this.headerEl.addClass('p-0');
19700         return this.headerEl
19701     },
19702     
19703     
19704     getAutoCreate : function(){
19705          
19706         var cfg = {
19707            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19708            style: 'display:block',
19709            cn : [
19710                 {
19711                     cls : 'arrow'
19712                 },
19713                 {
19714                     cls : 'popover-inner ',
19715                     cn : [
19716                         {
19717                             tag: 'h3',
19718                             cls: 'popover-title popover-header',
19719                             html : this.title === false ? '' : this.title
19720                         },
19721                         {
19722                             cls : 'popover-content popover-body '  + (this.cls || ''),
19723                             html : this.html || ''
19724                         }
19725                     ]
19726                     
19727                 }
19728            ]
19729         };
19730         
19731         return cfg;
19732     },
19733     /**
19734      * @param {string} the title
19735      */
19736     setTitle: function(str)
19737     {
19738         this.title = str;
19739         if (this.el) {
19740             this.headerEl.dom.innerHTML = str;
19741         }
19742         
19743     },
19744     /**
19745      * @param {string} the body content
19746      */
19747     setContent: function(str)
19748     {
19749         this.html = str;
19750         if (this.contentEl) {
19751             this.contentEl.dom.innerHTML = str;
19752         }
19753         
19754     },
19755     // as it get's added to the bottom of the page.
19756     onRender : function(ct, position)
19757     {
19758         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19759         
19760         
19761         
19762         if(!this.el){
19763             var cfg = Roo.apply({},  this.getAutoCreate());
19764             cfg.id = Roo.id();
19765             
19766             if (this.cls) {
19767                 cfg.cls += ' ' + this.cls;
19768             }
19769             if (this.style) {
19770                 cfg.style = this.style;
19771             }
19772             //Roo.log("adding to ");
19773             this.el = Roo.get(document.body).createChild(cfg, position);
19774 //            Roo.log(this.el);
19775         }
19776         
19777         this.contentEl = this.el.select('.popover-content',true).first();
19778         this.headerEl =  this.el.select('.popover-title',true).first();
19779         
19780         var nitems = [];
19781         if(typeof(this.items) != 'undefined'){
19782             var items = this.items;
19783             delete this.items;
19784
19785             for(var i =0;i < items.length;i++) {
19786                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19787             }
19788         }
19789
19790         this.items = nitems;
19791         
19792         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19793         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19794         
19795         
19796         
19797         this.initEvents();
19798     },
19799     
19800     resizeMask : function()
19801     {
19802         this.maskEl.setSize(
19803             Roo.lib.Dom.getViewWidth(true),
19804             Roo.lib.Dom.getViewHeight(true)
19805         );
19806     },
19807     
19808     initEvents : function()
19809     {
19810         
19811         if (!this.modal) { 
19812             Roo.bootstrap.Popover.register(this);
19813         }
19814          
19815         this.arrowEl = this.el.select('.arrow',true).first();
19816         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19817         this.el.enableDisplayMode('block');
19818         this.el.hide();
19819  
19820         
19821         if (this.over === false && !this.parent()) {
19822             return; 
19823         }
19824         if (this.triggers === false) {
19825             return;
19826         }
19827          
19828         // support parent
19829         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19830         var triggers = this.trigger ? this.trigger.split(' ') : [];
19831         Roo.each(triggers, function(trigger) {
19832         
19833             if (trigger == 'click') {
19834                 on_el.on('click', this.toggle, this);
19835             } else if (trigger != 'manual') {
19836                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19837                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19838       
19839                 on_el.on(eventIn  ,this.enter, this);
19840                 on_el.on(eventOut, this.leave, this);
19841             }
19842         }, this);
19843     },
19844     
19845     
19846     // private
19847     timeout : null,
19848     hoverState : null,
19849     
19850     toggle : function () {
19851         this.hoverState == 'in' ? this.leave() : this.enter();
19852     },
19853     
19854     enter : function () {
19855         
19856         clearTimeout(this.timeout);
19857     
19858         this.hoverState = 'in';
19859     
19860         if (!this.delay || !this.delay.show) {
19861             this.show();
19862             return;
19863         }
19864         var _t = this;
19865         this.timeout = setTimeout(function () {
19866             if (_t.hoverState == 'in') {
19867                 _t.show();
19868             }
19869         }, this.delay.show)
19870     },
19871     
19872     leave : function() {
19873         clearTimeout(this.timeout);
19874     
19875         this.hoverState = 'out';
19876     
19877         if (!this.delay || !this.delay.hide) {
19878             this.hide();
19879             return;
19880         }
19881         var _t = this;
19882         this.timeout = setTimeout(function () {
19883             if (_t.hoverState == 'out') {
19884                 _t.hide();
19885             }
19886         }, this.delay.hide)
19887     },
19888     /**
19889      * Show the popover
19890      * @param {Roo.Element|string|Boolean} - element to align and point to. (set align to [ pos, offset ])
19891      * @param {string} (left|right|top|bottom) position
19892      */
19893     show : function (on_el, placement)
19894     {
19895         this.placement = typeof(placement) == 'undefined' ?  this.placement   : placement;
19896         on_el = on_el || false; // default to false
19897          
19898         if (!on_el) {
19899             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19900                 on_el = this.parent().el;
19901             } else if (this.over) {
19902                 Roo.get(this.over);
19903             }
19904             
19905         }
19906         
19907         this.alignEl = Roo.get( on_el );
19908
19909         if (!this.el) {
19910             this.render(document.body);
19911         }
19912         
19913         
19914          
19915         
19916         if (this.title === false) {
19917             this.headerEl.hide();
19918         }
19919         
19920        
19921         this.el.show();
19922         this.el.dom.style.display = 'block';
19923          
19924  
19925         if (this.alignEl) {
19926             this.updatePosition(this.placement, true);
19927              
19928         } else {
19929             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19930             var es = this.el.getSize();
19931             var x = Roo.lib.Dom.getViewWidth()/2;
19932             var y = Roo.lib.Dom.getViewHeight()/2;
19933             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19934             
19935         }
19936
19937         
19938         //var arrow = this.el.select('.arrow',true).first();
19939         //arrow.set(align[2], 
19940         
19941         this.el.addClass('in');
19942         
19943          
19944         
19945         this.hoverState = 'in';
19946         
19947         if (this.modal) {
19948             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19949             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19950             this.maskEl.dom.style.display = 'block';
19951             this.maskEl.addClass('show');
19952         }
19953         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19954  
19955         this.fireEvent('show', this);
19956         
19957     },
19958     /**
19959      * fire this manually after loading a grid in the table for example
19960      * @param {string} (left|right|top|bottom) where to try and put it (use false to use the last one)
19961      * @param {Boolean} try and move it if we cant get right position.
19962      */
19963     updatePosition : function(placement, try_move)
19964     {
19965         // allow for calling with no parameters
19966         placement = placement   ? placement :  this.placement;
19967         try_move = typeof(try_move) == 'undefined' ? true : try_move;
19968         
19969         this.el.removeClass([
19970             'fade','top','bottom', 'left', 'right','in',
19971             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19972         ]);
19973         this.el.addClass(placement + ' bs-popover-' + placement);
19974         
19975         if (!this.alignEl ) {
19976             return false;
19977         }
19978         
19979         switch (placement) {
19980             case 'right':
19981                 var exact = this.el.getAlignToXY(this.alignEl, 'tl-tr', [10,0]);
19982                 var offset = this.el.getAlignToXY(this.alignEl, 'tl-tr?',[10,0]);
19983                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19984                     //normal display... or moved up/down.
19985                     this.el.setXY(offset);
19986                     var xy = this.alignEl.getAnchorXY('tr', false);
19987                     xy[0]+=2;xy[1]+=5;
19988                     this.arrowEl.setXY(xy);
19989                     return true;
19990                 }
19991                 // continue through...
19992                 return this.updatePosition('left', false);
19993                 
19994             
19995             case 'left':
19996                 var exact = this.el.getAlignToXY(this.alignEl, 'tr-tl', [-10,0]);
19997                 var offset = this.el.getAlignToXY(this.alignEl, 'tr-tl?',[-10,0]);
19998                 if (!try_move || exact.equals(offset) || exact[0] == offset[0] ) {
19999                     //normal display... or moved up/down.
20000                     this.el.setXY(offset);
20001                     var xy = this.alignEl.getAnchorXY('tl', false);
20002                     xy[0]-=10;xy[1]+=5; // << fix me
20003                     this.arrowEl.setXY(xy);
20004                     return true;
20005                 }
20006                 // call self...
20007                 return this.updatePosition('right', false);
20008             
20009             case 'top':
20010                 var exact = this.el.getAlignToXY(this.alignEl, 'b-t', [0,-10]);
20011                 var offset = this.el.getAlignToXY(this.alignEl, 'b-t?',[0,-10]);
20012                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20013                     //normal display... or moved up/down.
20014                     this.el.setXY(offset);
20015                     var xy = this.alignEl.getAnchorXY('t', false);
20016                     xy[1]-=10; // << fix me
20017                     this.arrowEl.setXY(xy);
20018                     return true;
20019                 }
20020                 // fall through
20021                return this.updatePosition('bottom', false);
20022             
20023             case 'bottom':
20024                  var exact = this.el.getAlignToXY(this.alignEl, 't-b', [0,10]);
20025                 var offset = this.el.getAlignToXY(this.alignEl, 't-b?',[0,10]);
20026                 if (!try_move || exact.equals(offset) || exact[1] == offset[1] ) {
20027                     //normal display... or moved up/down.
20028                     this.el.setXY(offset);
20029                     var xy = this.alignEl.getAnchorXY('b', false);
20030                      xy[1]+=2; // << fix me
20031                     this.arrowEl.setXY(xy);
20032                     return true;
20033                 }
20034                 // fall through
20035                 return this.updatePosition('top', false);
20036                 
20037             
20038         }
20039         
20040         
20041         return false;
20042     },
20043     
20044     hide : function()
20045     {
20046         this.el.setXY([0,0]);
20047         this.el.removeClass('in');
20048         this.el.hide();
20049         this.hoverState = null;
20050         this.maskEl.hide(); // always..
20051         this.fireEvent('hide', this);
20052     }
20053     
20054 });
20055
20056
20057 Roo.apply(Roo.bootstrap.Popover, {
20058
20059     alignment : {
20060         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
20061         'right' : ['l-br', [10,0], 'right bs-popover-right'],
20062         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
20063         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
20064     },
20065     
20066     zIndex : 20001,
20067
20068     clickHander : false,
20069     
20070
20071     onMouseDown : function(e)
20072     {
20073         if (!e.getTarget(".roo-popover")) {
20074             this.hideAll();
20075         }
20076          
20077     },
20078     
20079     popups : [],
20080     
20081     register : function(popup)
20082     {
20083         if (!Roo.bootstrap.Popover.clickHandler) {
20084             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
20085         }
20086         // hide other popups.
20087         this.hideAll();
20088         this.popups.push(popup);
20089     },
20090     hideAll : function()
20091     {
20092         this.popups.forEach(function(p) {
20093             p.hide();
20094         });
20095     }
20096
20097 });/*
20098  * - LGPL
20099  *
20100  * Card header - holder for the card header elements.
20101  * 
20102  */
20103
20104 /**
20105  * @class Roo.bootstrap.PopoverNav
20106  * @extends Roo.bootstrap.NavGroup
20107  * Bootstrap Popover header navigation class
20108  * @constructor
20109  * Create a new Popover Header Navigation 
20110  * @param {Object} config The config object
20111  */
20112
20113 Roo.bootstrap.PopoverNav = function(config){
20114     Roo.bootstrap.PopoverNav.superclass.constructor.call(this, config);
20115 };
20116
20117 Roo.extend(Roo.bootstrap.PopoverNav, Roo.bootstrap.NavSimplebar,  {
20118     
20119     
20120     container_method : 'getPopoverHeader' 
20121     
20122      
20123     
20124     
20125    
20126 });
20127
20128  
20129
20130  /*
20131  * - LGPL
20132  *
20133  * Progress
20134  * 
20135  */
20136
20137 /**
20138  * @class Roo.bootstrap.Progress
20139  * @extends Roo.bootstrap.Component
20140  * Bootstrap Progress class
20141  * @cfg {Boolean} striped striped of the progress bar
20142  * @cfg {Boolean} active animated of the progress bar
20143  * 
20144  * 
20145  * @constructor
20146  * Create a new Progress
20147  * @param {Object} config The config object
20148  */
20149
20150 Roo.bootstrap.Progress = function(config){
20151     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20152 };
20153
20154 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20155     
20156     striped : false,
20157     active: false,
20158     
20159     getAutoCreate : function(){
20160         var cfg = {
20161             tag: 'div',
20162             cls: 'progress'
20163         };
20164         
20165         
20166         if(this.striped){
20167             cfg.cls += ' progress-striped';
20168         }
20169       
20170         if(this.active){
20171             cfg.cls += ' active';
20172         }
20173         
20174         
20175         return cfg;
20176     }
20177    
20178 });
20179
20180  
20181
20182  /*
20183  * - LGPL
20184  *
20185  * ProgressBar
20186  * 
20187  */
20188
20189 /**
20190  * @class Roo.bootstrap.ProgressBar
20191  * @extends Roo.bootstrap.Component
20192  * Bootstrap ProgressBar class
20193  * @cfg {Number} aria_valuenow aria-value now
20194  * @cfg {Number} aria_valuemin aria-value min
20195  * @cfg {Number} aria_valuemax aria-value max
20196  * @cfg {String} label label for the progress bar
20197  * @cfg {String} panel (success | info | warning | danger )
20198  * @cfg {String} role role of the progress bar
20199  * @cfg {String} sr_only text
20200  * 
20201  * 
20202  * @constructor
20203  * Create a new ProgressBar
20204  * @param {Object} config The config object
20205  */
20206
20207 Roo.bootstrap.ProgressBar = function(config){
20208     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20209 };
20210
20211 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20212     
20213     aria_valuenow : 0,
20214     aria_valuemin : 0,
20215     aria_valuemax : 100,
20216     label : false,
20217     panel : false,
20218     role : false,
20219     sr_only: false,
20220     
20221     getAutoCreate : function()
20222     {
20223         
20224         var cfg = {
20225             tag: 'div',
20226             cls: 'progress-bar',
20227             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20228         };
20229         
20230         if(this.sr_only){
20231             cfg.cn = {
20232                 tag: 'span',
20233                 cls: 'sr-only',
20234                 html: this.sr_only
20235             }
20236         }
20237         
20238         if(this.role){
20239             cfg.role = this.role;
20240         }
20241         
20242         if(this.aria_valuenow){
20243             cfg['aria-valuenow'] = this.aria_valuenow;
20244         }
20245         
20246         if(this.aria_valuemin){
20247             cfg['aria-valuemin'] = this.aria_valuemin;
20248         }
20249         
20250         if(this.aria_valuemax){
20251             cfg['aria-valuemax'] = this.aria_valuemax;
20252         }
20253         
20254         if(this.label && !this.sr_only){
20255             cfg.html = this.label;
20256         }
20257         
20258         if(this.panel){
20259             cfg.cls += ' progress-bar-' + this.panel;
20260         }
20261         
20262         return cfg;
20263     },
20264     
20265     update : function(aria_valuenow)
20266     {
20267         this.aria_valuenow = aria_valuenow;
20268         
20269         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20270     }
20271    
20272 });
20273
20274  
20275
20276  /*
20277  * - LGPL
20278  *
20279  * column
20280  * 
20281  */
20282
20283 /**
20284  * @class Roo.bootstrap.TabGroup
20285  * @extends Roo.bootstrap.Column
20286  * Bootstrap Column class
20287  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20288  * @cfg {Boolean} carousel true to make the group behave like a carousel
20289  * @cfg {Boolean} bullets show bullets for the panels
20290  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20291  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20292  * @cfg {Boolean} showarrow (true|false) show arrow default true
20293  * 
20294  * @constructor
20295  * Create a new TabGroup
20296  * @param {Object} config The config object
20297  */
20298
20299 Roo.bootstrap.TabGroup = function(config){
20300     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20301     if (!this.navId) {
20302         this.navId = Roo.id();
20303     }
20304     this.tabs = [];
20305     Roo.bootstrap.TabGroup.register(this);
20306     
20307 };
20308
20309 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20310     
20311     carousel : false,
20312     transition : false,
20313     bullets : 0,
20314     timer : 0,
20315     autoslide : false,
20316     slideFn : false,
20317     slideOnTouch : false,
20318     showarrow : true,
20319     
20320     getAutoCreate : function()
20321     {
20322         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20323         
20324         cfg.cls += ' tab-content';
20325         
20326         if (this.carousel) {
20327             cfg.cls += ' carousel slide';
20328             
20329             cfg.cn = [{
20330                cls : 'carousel-inner',
20331                cn : []
20332             }];
20333         
20334             if(this.bullets  && !Roo.isTouch){
20335                 
20336                 var bullets = {
20337                     cls : 'carousel-bullets',
20338                     cn : []
20339                 };
20340                
20341                 if(this.bullets_cls){
20342                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20343                 }
20344                 
20345                 bullets.cn.push({
20346                     cls : 'clear'
20347                 });
20348                 
20349                 cfg.cn[0].cn.push(bullets);
20350             }
20351             
20352             if(this.showarrow){
20353                 cfg.cn[0].cn.push({
20354                     tag : 'div',
20355                     class : 'carousel-arrow',
20356                     cn : [
20357                         {
20358                             tag : 'div',
20359                             class : 'carousel-prev',
20360                             cn : [
20361                                 {
20362                                     tag : 'i',
20363                                     class : 'fa fa-chevron-left'
20364                                 }
20365                             ]
20366                         },
20367                         {
20368                             tag : 'div',
20369                             class : 'carousel-next',
20370                             cn : [
20371                                 {
20372                                     tag : 'i',
20373                                     class : 'fa fa-chevron-right'
20374                                 }
20375                             ]
20376                         }
20377                     ]
20378                 });
20379             }
20380             
20381         }
20382         
20383         return cfg;
20384     },
20385     
20386     initEvents:  function()
20387     {
20388 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20389 //            this.el.on("touchstart", this.onTouchStart, this);
20390 //        }
20391         
20392         if(this.autoslide){
20393             var _this = this;
20394             
20395             this.slideFn = window.setInterval(function() {
20396                 _this.showPanelNext();
20397             }, this.timer);
20398         }
20399         
20400         if(this.showarrow){
20401             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20402             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20403         }
20404         
20405         
20406     },
20407     
20408 //    onTouchStart : function(e, el, o)
20409 //    {
20410 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20411 //            return;
20412 //        }
20413 //        
20414 //        this.showPanelNext();
20415 //    },
20416     
20417     
20418     getChildContainer : function()
20419     {
20420         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20421     },
20422     
20423     /**
20424     * register a Navigation item
20425     * @param {Roo.bootstrap.NavItem} the navitem to add
20426     */
20427     register : function(item)
20428     {
20429         this.tabs.push( item);
20430         item.navId = this.navId; // not really needed..
20431         this.addBullet();
20432     
20433     },
20434     
20435     getActivePanel : function()
20436     {
20437         var r = false;
20438         Roo.each(this.tabs, function(t) {
20439             if (t.active) {
20440                 r = t;
20441                 return false;
20442             }
20443             return null;
20444         });
20445         return r;
20446         
20447     },
20448     getPanelByName : function(n)
20449     {
20450         var r = false;
20451         Roo.each(this.tabs, function(t) {
20452             if (t.tabId == n) {
20453                 r = t;
20454                 return false;
20455             }
20456             return null;
20457         });
20458         return r;
20459     },
20460     indexOfPanel : function(p)
20461     {
20462         var r = false;
20463         Roo.each(this.tabs, function(t,i) {
20464             if (t.tabId == p.tabId) {
20465                 r = i;
20466                 return false;
20467             }
20468             return null;
20469         });
20470         return r;
20471     },
20472     /**
20473      * show a specific panel
20474      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20475      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20476      */
20477     showPanel : function (pan)
20478     {
20479         if(this.transition || typeof(pan) == 'undefined'){
20480             Roo.log("waiting for the transitionend");
20481             return false;
20482         }
20483         
20484         if (typeof(pan) == 'number') {
20485             pan = this.tabs[pan];
20486         }
20487         
20488         if (typeof(pan) == 'string') {
20489             pan = this.getPanelByName(pan);
20490         }
20491         
20492         var cur = this.getActivePanel();
20493         
20494         if(!pan || !cur){
20495             Roo.log('pan or acitve pan is undefined');
20496             return false;
20497         }
20498         
20499         if (pan.tabId == this.getActivePanel().tabId) {
20500             return true;
20501         }
20502         
20503         if (false === cur.fireEvent('beforedeactivate')) {
20504             return false;
20505         }
20506         
20507         if(this.bullets > 0 && !Roo.isTouch){
20508             this.setActiveBullet(this.indexOfPanel(pan));
20509         }
20510         
20511         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20512             
20513             //class="carousel-item carousel-item-next carousel-item-left"
20514             
20515             this.transition = true;
20516             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20517             var lr = dir == 'next' ? 'left' : 'right';
20518             pan.el.addClass(dir); // or prev
20519             pan.el.addClass('carousel-item-' + dir); // or prev
20520             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20521             cur.el.addClass(lr); // or right
20522             pan.el.addClass(lr);
20523             cur.el.addClass('carousel-item-' +lr); // or right
20524             pan.el.addClass('carousel-item-' +lr);
20525             
20526             
20527             var _this = this;
20528             cur.el.on('transitionend', function() {
20529                 Roo.log("trans end?");
20530                 
20531                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20532                 pan.setActive(true);
20533                 
20534                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20535                 cur.setActive(false);
20536                 
20537                 _this.transition = false;
20538                 
20539             }, this, { single:  true } );
20540             
20541             return true;
20542         }
20543         
20544         cur.setActive(false);
20545         pan.setActive(true);
20546         
20547         return true;
20548         
20549     },
20550     showPanelNext : function()
20551     {
20552         var i = this.indexOfPanel(this.getActivePanel());
20553         
20554         if (i >= this.tabs.length - 1 && !this.autoslide) {
20555             return;
20556         }
20557         
20558         if (i >= this.tabs.length - 1 && this.autoslide) {
20559             i = -1;
20560         }
20561         
20562         this.showPanel(this.tabs[i+1]);
20563     },
20564     
20565     showPanelPrev : function()
20566     {
20567         var i = this.indexOfPanel(this.getActivePanel());
20568         
20569         if (i  < 1 && !this.autoslide) {
20570             return;
20571         }
20572         
20573         if (i < 1 && this.autoslide) {
20574             i = this.tabs.length;
20575         }
20576         
20577         this.showPanel(this.tabs[i-1]);
20578     },
20579     
20580     
20581     addBullet: function()
20582     {
20583         if(!this.bullets || Roo.isTouch){
20584             return;
20585         }
20586         var ctr = this.el.select('.carousel-bullets',true).first();
20587         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20588         var bullet = ctr.createChild({
20589             cls : 'bullet bullet-' + i
20590         },ctr.dom.lastChild);
20591         
20592         
20593         var _this = this;
20594         
20595         bullet.on('click', (function(e, el, o, ii, t){
20596
20597             e.preventDefault();
20598
20599             this.showPanel(ii);
20600
20601             if(this.autoslide && this.slideFn){
20602                 clearInterval(this.slideFn);
20603                 this.slideFn = window.setInterval(function() {
20604                     _this.showPanelNext();
20605                 }, this.timer);
20606             }
20607
20608         }).createDelegate(this, [i, bullet], true));
20609                 
20610         
20611     },
20612      
20613     setActiveBullet : function(i)
20614     {
20615         if(Roo.isTouch){
20616             return;
20617         }
20618         
20619         Roo.each(this.el.select('.bullet', true).elements, function(el){
20620             el.removeClass('selected');
20621         });
20622
20623         var bullet = this.el.select('.bullet-' + i, true).first();
20624         
20625         if(!bullet){
20626             return;
20627         }
20628         
20629         bullet.addClass('selected');
20630     }
20631     
20632     
20633   
20634 });
20635
20636  
20637
20638  
20639  
20640 Roo.apply(Roo.bootstrap.TabGroup, {
20641     
20642     groups: {},
20643      /**
20644     * register a Navigation Group
20645     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20646     */
20647     register : function(navgrp)
20648     {
20649         this.groups[navgrp.navId] = navgrp;
20650         
20651     },
20652     /**
20653     * fetch a Navigation Group based on the navigation ID
20654     * if one does not exist , it will get created.
20655     * @param {string} the navgroup to add
20656     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20657     */
20658     get: function(navId) {
20659         if (typeof(this.groups[navId]) == 'undefined') {
20660             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20661         }
20662         return this.groups[navId] ;
20663     }
20664     
20665     
20666     
20667 });
20668
20669  /*
20670  * - LGPL
20671  *
20672  * TabPanel
20673  * 
20674  */
20675
20676 /**
20677  * @class Roo.bootstrap.TabPanel
20678  * @extends Roo.bootstrap.Component
20679  * Bootstrap TabPanel class
20680  * @cfg {Boolean} active panel active
20681  * @cfg {String} html panel content
20682  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20683  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20684  * @cfg {String} href click to link..
20685  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20686  * 
20687  * 
20688  * @constructor
20689  * Create a new TabPanel
20690  * @param {Object} config The config object
20691  */
20692
20693 Roo.bootstrap.TabPanel = function(config){
20694     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20695     this.addEvents({
20696         /**
20697              * @event changed
20698              * Fires when the active status changes
20699              * @param {Roo.bootstrap.TabPanel} this
20700              * @param {Boolean} state the new state
20701             
20702          */
20703         'changed': true,
20704         /**
20705              * @event beforedeactivate
20706              * Fires before a tab is de-activated - can be used to do validation on a form.
20707              * @param {Roo.bootstrap.TabPanel} this
20708              * @return {Boolean} false if there is an error
20709             
20710          */
20711         'beforedeactivate': true
20712      });
20713     
20714     this.tabId = this.tabId || Roo.id();
20715   
20716 };
20717
20718 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20719     
20720     active: false,
20721     html: false,
20722     tabId: false,
20723     navId : false,
20724     href : '',
20725     touchSlide : false,
20726     getAutoCreate : function(){
20727         
20728         
20729         var cfg = {
20730             tag: 'div',
20731             // item is needed for carousel - not sure if it has any effect otherwise
20732             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20733             html: this.html || ''
20734         };
20735         
20736         if(this.active){
20737             cfg.cls += ' active';
20738         }
20739         
20740         if(this.tabId){
20741             cfg.tabId = this.tabId;
20742         }
20743         
20744         
20745         
20746         return cfg;
20747     },
20748     
20749     initEvents:  function()
20750     {
20751         var p = this.parent();
20752         
20753         this.navId = this.navId || p.navId;
20754         
20755         if (typeof(this.navId) != 'undefined') {
20756             // not really needed.. but just in case.. parent should be a NavGroup.
20757             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20758             
20759             tg.register(this);
20760             
20761             var i = tg.tabs.length - 1;
20762             
20763             if(this.active && tg.bullets > 0 && i < tg.bullets){
20764                 tg.setActiveBullet(i);
20765             }
20766         }
20767         
20768         this.el.on('click', this.onClick, this);
20769         
20770         if(Roo.isTouch && this.touchSlide){
20771             this.el.on("touchstart", this.onTouchStart, this);
20772             this.el.on("touchmove", this.onTouchMove, this);
20773             this.el.on("touchend", this.onTouchEnd, this);
20774         }
20775         
20776     },
20777     
20778     onRender : function(ct, position)
20779     {
20780         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20781     },
20782     
20783     setActive : function(state)
20784     {
20785         Roo.log("panel - set active " + this.tabId + "=" + state);
20786         
20787         this.active = state;
20788         if (!state) {
20789             this.el.removeClass('active');
20790             
20791         } else  if (!this.el.hasClass('active')) {
20792             this.el.addClass('active');
20793         }
20794         
20795         this.fireEvent('changed', this, state);
20796     },
20797     
20798     onClick : function(e)
20799     {
20800         e.preventDefault();
20801         
20802         if(!this.href.length){
20803             return;
20804         }
20805         
20806         window.location.href = this.href;
20807     },
20808     
20809     startX : 0,
20810     startY : 0,
20811     endX : 0,
20812     endY : 0,
20813     swiping : false,
20814     
20815     onTouchStart : function(e)
20816     {
20817         this.swiping = false;
20818         
20819         this.startX = e.browserEvent.touches[0].clientX;
20820         this.startY = e.browserEvent.touches[0].clientY;
20821     },
20822     
20823     onTouchMove : function(e)
20824     {
20825         this.swiping = true;
20826         
20827         this.endX = e.browserEvent.touches[0].clientX;
20828         this.endY = e.browserEvent.touches[0].clientY;
20829     },
20830     
20831     onTouchEnd : function(e)
20832     {
20833         if(!this.swiping){
20834             this.onClick(e);
20835             return;
20836         }
20837         
20838         var tabGroup = this.parent();
20839         
20840         if(this.endX > this.startX){ // swiping right
20841             tabGroup.showPanelPrev();
20842             return;
20843         }
20844         
20845         if(this.startX > this.endX){ // swiping left
20846             tabGroup.showPanelNext();
20847             return;
20848         }
20849     }
20850     
20851     
20852 });
20853  
20854
20855  
20856
20857  /*
20858  * - LGPL
20859  *
20860  * DateField
20861  * 
20862  */
20863
20864 /**
20865  * @class Roo.bootstrap.DateField
20866  * @extends Roo.bootstrap.Input
20867  * Bootstrap DateField class
20868  * @cfg {Number} weekStart default 0
20869  * @cfg {String} viewMode default empty, (months|years)
20870  * @cfg {String} minViewMode default empty, (months|years)
20871  * @cfg {Number} startDate default -Infinity
20872  * @cfg {Number} endDate default Infinity
20873  * @cfg {Boolean} todayHighlight default false
20874  * @cfg {Boolean} todayBtn default false
20875  * @cfg {Boolean} calendarWeeks default false
20876  * @cfg {Object} daysOfWeekDisabled default empty
20877  * @cfg {Boolean} singleMode default false (true | false)
20878  * 
20879  * @cfg {Boolean} keyboardNavigation default true
20880  * @cfg {String} language default en
20881  * 
20882  * @constructor
20883  * Create a new DateField
20884  * @param {Object} config The config object
20885  */
20886
20887 Roo.bootstrap.DateField = function(config){
20888     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20889      this.addEvents({
20890             /**
20891              * @event show
20892              * Fires when this field show.
20893              * @param {Roo.bootstrap.DateField} this
20894              * @param {Mixed} date The date value
20895              */
20896             show : true,
20897             /**
20898              * @event show
20899              * Fires when this field hide.
20900              * @param {Roo.bootstrap.DateField} this
20901              * @param {Mixed} date The date value
20902              */
20903             hide : true,
20904             /**
20905              * @event select
20906              * Fires when select a date.
20907              * @param {Roo.bootstrap.DateField} this
20908              * @param {Mixed} date The date value
20909              */
20910             select : true,
20911             /**
20912              * @event beforeselect
20913              * Fires when before select a date.
20914              * @param {Roo.bootstrap.DateField} this
20915              * @param {Mixed} date The date value
20916              */
20917             beforeselect : true
20918         });
20919 };
20920
20921 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20922     
20923     /**
20924      * @cfg {String} format
20925      * The default date format string which can be overriden for localization support.  The format must be
20926      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20927      */
20928     format : "m/d/y",
20929     /**
20930      * @cfg {String} altFormats
20931      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20932      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20933      */
20934     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20935     
20936     weekStart : 0,
20937     
20938     viewMode : '',
20939     
20940     minViewMode : '',
20941     
20942     todayHighlight : false,
20943     
20944     todayBtn: false,
20945     
20946     language: 'en',
20947     
20948     keyboardNavigation: true,
20949     
20950     calendarWeeks: false,
20951     
20952     startDate: -Infinity,
20953     
20954     endDate: Infinity,
20955     
20956     daysOfWeekDisabled: [],
20957     
20958     _events: [],
20959     
20960     singleMode : false,
20961     
20962     UTCDate: function()
20963     {
20964         return new Date(Date.UTC.apply(Date, arguments));
20965     },
20966     
20967     UTCToday: function()
20968     {
20969         var today = new Date();
20970         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20971     },
20972     
20973     getDate: function() {
20974             var d = this.getUTCDate();
20975             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20976     },
20977     
20978     getUTCDate: function() {
20979             return this.date;
20980     },
20981     
20982     setDate: function(d) {
20983             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20984     },
20985     
20986     setUTCDate: function(d) {
20987             this.date = d;
20988             this.setValue(this.formatDate(this.date));
20989     },
20990         
20991     onRender: function(ct, position)
20992     {
20993         
20994         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20995         
20996         this.language = this.language || 'en';
20997         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20998         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20999         
21000         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
21001         this.format = this.format || 'm/d/y';
21002         this.isInline = false;
21003         this.isInput = true;
21004         this.component = this.el.select('.add-on', true).first() || false;
21005         this.component = (this.component && this.component.length === 0) ? false : this.component;
21006         this.hasInput = this.component && this.inputEl().length;
21007         
21008         if (typeof(this.minViewMode === 'string')) {
21009             switch (this.minViewMode) {
21010                 case 'months':
21011                     this.minViewMode = 1;
21012                     break;
21013                 case 'years':
21014                     this.minViewMode = 2;
21015                     break;
21016                 default:
21017                     this.minViewMode = 0;
21018                     break;
21019             }
21020         }
21021         
21022         if (typeof(this.viewMode === 'string')) {
21023             switch (this.viewMode) {
21024                 case 'months':
21025                     this.viewMode = 1;
21026                     break;
21027                 case 'years':
21028                     this.viewMode = 2;
21029                     break;
21030                 default:
21031                     this.viewMode = 0;
21032                     break;
21033             }
21034         }
21035                 
21036         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
21037         
21038 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
21039         
21040         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21041         
21042         this.picker().on('mousedown', this.onMousedown, this);
21043         this.picker().on('click', this.onClick, this);
21044         
21045         this.picker().addClass('datepicker-dropdown');
21046         
21047         this.startViewMode = this.viewMode;
21048         
21049         if(this.singleMode){
21050             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
21051                 v.setVisibilityMode(Roo.Element.DISPLAY);
21052                 v.hide();
21053             });
21054             
21055             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
21056                 v.setStyle('width', '189px');
21057             });
21058         }
21059         
21060         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
21061             if(!this.calendarWeeks){
21062                 v.remove();
21063                 return;
21064             }
21065             
21066             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21067             v.attr('colspan', function(i, val){
21068                 return parseInt(val) + 1;
21069             });
21070         });
21071                         
21072         
21073         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
21074         
21075         this.setStartDate(this.startDate);
21076         this.setEndDate(this.endDate);
21077         
21078         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
21079         
21080         this.fillDow();
21081         this.fillMonths();
21082         this.update();
21083         this.showMode();
21084         
21085         if(this.isInline) {
21086             this.showPopup();
21087         }
21088     },
21089     
21090     picker : function()
21091     {
21092         return this.pickerEl;
21093 //        return this.el.select('.datepicker', true).first();
21094     },
21095     
21096     fillDow: function()
21097     {
21098         var dowCnt = this.weekStart;
21099         
21100         var dow = {
21101             tag: 'tr',
21102             cn: [
21103                 
21104             ]
21105         };
21106         
21107         if(this.calendarWeeks){
21108             dow.cn.push({
21109                 tag: 'th',
21110                 cls: 'cw',
21111                 html: '&nbsp;'
21112             })
21113         }
21114         
21115         while (dowCnt < this.weekStart + 7) {
21116             dow.cn.push({
21117                 tag: 'th',
21118                 cls: 'dow',
21119                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
21120             });
21121         }
21122         
21123         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
21124     },
21125     
21126     fillMonths: function()
21127     {    
21128         var i = 0;
21129         var months = this.picker().select('>.datepicker-months td', true).first();
21130         
21131         months.dom.innerHTML = '';
21132         
21133         while (i < 12) {
21134             var month = {
21135                 tag: 'span',
21136                 cls: 'month',
21137                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21138             };
21139             
21140             months.createChild(month);
21141         }
21142         
21143     },
21144     
21145     update: function()
21146     {
21147         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;
21148         
21149         if (this.date < this.startDate) {
21150             this.viewDate = new Date(this.startDate);
21151         } else if (this.date > this.endDate) {
21152             this.viewDate = new Date(this.endDate);
21153         } else {
21154             this.viewDate = new Date(this.date);
21155         }
21156         
21157         this.fill();
21158     },
21159     
21160     fill: function() 
21161     {
21162         var d = new Date(this.viewDate),
21163                 year = d.getUTCFullYear(),
21164                 month = d.getUTCMonth(),
21165                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21166                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21167                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21168                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21169                 currentDate = this.date && this.date.valueOf(),
21170                 today = this.UTCToday();
21171         
21172         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21173         
21174 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21175         
21176 //        this.picker.select('>tfoot th.today').
21177 //                                              .text(dates[this.language].today)
21178 //                                              .toggle(this.todayBtn !== false);
21179     
21180         this.updateNavArrows();
21181         this.fillMonths();
21182                                                 
21183         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21184         
21185         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21186          
21187         prevMonth.setUTCDate(day);
21188         
21189         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21190         
21191         var nextMonth = new Date(prevMonth);
21192         
21193         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21194         
21195         nextMonth = nextMonth.valueOf();
21196         
21197         var fillMonths = false;
21198         
21199         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21200         
21201         while(prevMonth.valueOf() <= nextMonth) {
21202             var clsName = '';
21203             
21204             if (prevMonth.getUTCDay() === this.weekStart) {
21205                 if(fillMonths){
21206                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21207                 }
21208                     
21209                 fillMonths = {
21210                     tag: 'tr',
21211                     cn: []
21212                 };
21213                 
21214                 if(this.calendarWeeks){
21215                     // ISO 8601: First week contains first thursday.
21216                     // ISO also states week starts on Monday, but we can be more abstract here.
21217                     var
21218                     // Start of current week: based on weekstart/current date
21219                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21220                     // Thursday of this week
21221                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21222                     // First Thursday of year, year from thursday
21223                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21224                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21225                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21226                     
21227                     fillMonths.cn.push({
21228                         tag: 'td',
21229                         cls: 'cw',
21230                         html: calWeek
21231                     });
21232                 }
21233             }
21234             
21235             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21236                 clsName += ' old';
21237             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21238                 clsName += ' new';
21239             }
21240             if (this.todayHighlight &&
21241                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21242                 prevMonth.getUTCMonth() == today.getMonth() &&
21243                 prevMonth.getUTCDate() == today.getDate()) {
21244                 clsName += ' today';
21245             }
21246             
21247             if (currentDate && prevMonth.valueOf() === currentDate) {
21248                 clsName += ' active';
21249             }
21250             
21251             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21252                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21253                     clsName += ' disabled';
21254             }
21255             
21256             fillMonths.cn.push({
21257                 tag: 'td',
21258                 cls: 'day ' + clsName,
21259                 html: prevMonth.getDate()
21260             });
21261             
21262             prevMonth.setDate(prevMonth.getDate()+1);
21263         }
21264           
21265         var currentYear = this.date && this.date.getUTCFullYear();
21266         var currentMonth = this.date && this.date.getUTCMonth();
21267         
21268         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21269         
21270         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21271             v.removeClass('active');
21272             
21273             if(currentYear === year && k === currentMonth){
21274                 v.addClass('active');
21275             }
21276             
21277             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21278                 v.addClass('disabled');
21279             }
21280             
21281         });
21282         
21283         
21284         year = parseInt(year/10, 10) * 10;
21285         
21286         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21287         
21288         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21289         
21290         year -= 1;
21291         for (var i = -1; i < 11; i++) {
21292             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21293                 tag: 'span',
21294                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21295                 html: year
21296             });
21297             
21298             year += 1;
21299         }
21300     },
21301     
21302     showMode: function(dir) 
21303     {
21304         if (dir) {
21305             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21306         }
21307         
21308         Roo.each(this.picker().select('>div',true).elements, function(v){
21309             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21310             v.hide();
21311         });
21312         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21313     },
21314     
21315     place: function()
21316     {
21317         if(this.isInline) {
21318             return;
21319         }
21320         
21321         this.picker().removeClass(['bottom', 'top']);
21322         
21323         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21324             /*
21325              * place to the top of element!
21326              *
21327              */
21328             
21329             this.picker().addClass('top');
21330             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21331             
21332             return;
21333         }
21334         
21335         this.picker().addClass('bottom');
21336         
21337         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21338     },
21339     
21340     parseDate : function(value)
21341     {
21342         if(!value || value instanceof Date){
21343             return value;
21344         }
21345         var v = Date.parseDate(value, this.format);
21346         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21347             v = Date.parseDate(value, 'Y-m-d');
21348         }
21349         if(!v && this.altFormats){
21350             if(!this.altFormatsArray){
21351                 this.altFormatsArray = this.altFormats.split("|");
21352             }
21353             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21354                 v = Date.parseDate(value, this.altFormatsArray[i]);
21355             }
21356         }
21357         return v;
21358     },
21359     
21360     formatDate : function(date, fmt)
21361     {   
21362         return (!date || !(date instanceof Date)) ?
21363         date : date.dateFormat(fmt || this.format);
21364     },
21365     
21366     onFocus : function()
21367     {
21368         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21369         this.showPopup();
21370     },
21371     
21372     onBlur : function()
21373     {
21374         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21375         
21376         var d = this.inputEl().getValue();
21377         
21378         this.setValue(d);
21379                 
21380         this.hidePopup();
21381     },
21382     
21383     showPopup : function()
21384     {
21385         this.picker().show();
21386         this.update();
21387         this.place();
21388         
21389         this.fireEvent('showpopup', this, this.date);
21390     },
21391     
21392     hidePopup : function()
21393     {
21394         if(this.isInline) {
21395             return;
21396         }
21397         this.picker().hide();
21398         this.viewMode = this.startViewMode;
21399         this.showMode();
21400         
21401         this.fireEvent('hidepopup', this, this.date);
21402         
21403     },
21404     
21405     onMousedown: function(e)
21406     {
21407         e.stopPropagation();
21408         e.preventDefault();
21409     },
21410     
21411     keyup: function(e)
21412     {
21413         Roo.bootstrap.DateField.superclass.keyup.call(this);
21414         this.update();
21415     },
21416
21417     setValue: function(v)
21418     {
21419         if(this.fireEvent('beforeselect', this, v) !== false){
21420             var d = new Date(this.parseDate(v) ).clearTime();
21421         
21422             if(isNaN(d.getTime())){
21423                 this.date = this.viewDate = '';
21424                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21425                 return;
21426             }
21427
21428             v = this.formatDate(d);
21429
21430             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21431
21432             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21433
21434             this.update();
21435
21436             this.fireEvent('select', this, this.date);
21437         }
21438     },
21439     
21440     getValue: function()
21441     {
21442         return this.formatDate(this.date);
21443     },
21444     
21445     fireKey: function(e)
21446     {
21447         if (!this.picker().isVisible()){
21448             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21449                 this.showPopup();
21450             }
21451             return;
21452         }
21453         
21454         var dateChanged = false,
21455         dir, day, month,
21456         newDate, newViewDate;
21457         
21458         switch(e.keyCode){
21459             case 27: // escape
21460                 this.hidePopup();
21461                 e.preventDefault();
21462                 break;
21463             case 37: // left
21464             case 39: // right
21465                 if (!this.keyboardNavigation) {
21466                     break;
21467                 }
21468                 dir = e.keyCode == 37 ? -1 : 1;
21469                 
21470                 if (e.ctrlKey){
21471                     newDate = this.moveYear(this.date, dir);
21472                     newViewDate = this.moveYear(this.viewDate, dir);
21473                 } else if (e.shiftKey){
21474                     newDate = this.moveMonth(this.date, dir);
21475                     newViewDate = this.moveMonth(this.viewDate, dir);
21476                 } else {
21477                     newDate = new Date(this.date);
21478                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21479                     newViewDate = new Date(this.viewDate);
21480                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21481                 }
21482                 if (this.dateWithinRange(newDate)){
21483                     this.date = newDate;
21484                     this.viewDate = newViewDate;
21485                     this.setValue(this.formatDate(this.date));
21486 //                    this.update();
21487                     e.preventDefault();
21488                     dateChanged = true;
21489                 }
21490                 break;
21491             case 38: // up
21492             case 40: // down
21493                 if (!this.keyboardNavigation) {
21494                     break;
21495                 }
21496                 dir = e.keyCode == 38 ? -1 : 1;
21497                 if (e.ctrlKey){
21498                     newDate = this.moveYear(this.date, dir);
21499                     newViewDate = this.moveYear(this.viewDate, dir);
21500                 } else if (e.shiftKey){
21501                     newDate = this.moveMonth(this.date, dir);
21502                     newViewDate = this.moveMonth(this.viewDate, dir);
21503                 } else {
21504                     newDate = new Date(this.date);
21505                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21506                     newViewDate = new Date(this.viewDate);
21507                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21508                 }
21509                 if (this.dateWithinRange(newDate)){
21510                     this.date = newDate;
21511                     this.viewDate = newViewDate;
21512                     this.setValue(this.formatDate(this.date));
21513 //                    this.update();
21514                     e.preventDefault();
21515                     dateChanged = true;
21516                 }
21517                 break;
21518             case 13: // enter
21519                 this.setValue(this.formatDate(this.date));
21520                 this.hidePopup();
21521                 e.preventDefault();
21522                 break;
21523             case 9: // tab
21524                 this.setValue(this.formatDate(this.date));
21525                 this.hidePopup();
21526                 break;
21527             case 16: // shift
21528             case 17: // ctrl
21529             case 18: // alt
21530                 break;
21531             default :
21532                 this.hidePopup();
21533                 
21534         }
21535     },
21536     
21537     
21538     onClick: function(e) 
21539     {
21540         e.stopPropagation();
21541         e.preventDefault();
21542         
21543         var target = e.getTarget();
21544         
21545         if(target.nodeName.toLowerCase() === 'i'){
21546             target = Roo.get(target).dom.parentNode;
21547         }
21548         
21549         var nodeName = target.nodeName;
21550         var className = target.className;
21551         var html = target.innerHTML;
21552         //Roo.log(nodeName);
21553         
21554         switch(nodeName.toLowerCase()) {
21555             case 'th':
21556                 switch(className) {
21557                     case 'switch':
21558                         this.showMode(1);
21559                         break;
21560                     case 'prev':
21561                     case 'next':
21562                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21563                         switch(this.viewMode){
21564                                 case 0:
21565                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21566                                         break;
21567                                 case 1:
21568                                 case 2:
21569                                         this.viewDate = this.moveYear(this.viewDate, dir);
21570                                         break;
21571                         }
21572                         this.fill();
21573                         break;
21574                     case 'today':
21575                         var date = new Date();
21576                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21577 //                        this.fill()
21578                         this.setValue(this.formatDate(this.date));
21579                         
21580                         this.hidePopup();
21581                         break;
21582                 }
21583                 break;
21584             case 'span':
21585                 if (className.indexOf('disabled') < 0) {
21586                     this.viewDate.setUTCDate(1);
21587                     if (className.indexOf('month') > -1) {
21588                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21589                     } else {
21590                         var year = parseInt(html, 10) || 0;
21591                         this.viewDate.setUTCFullYear(year);
21592                         
21593                     }
21594                     
21595                     if(this.singleMode){
21596                         this.setValue(this.formatDate(this.viewDate));
21597                         this.hidePopup();
21598                         return;
21599                     }
21600                     
21601                     this.showMode(-1);
21602                     this.fill();
21603                 }
21604                 break;
21605                 
21606             case 'td':
21607                 //Roo.log(className);
21608                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21609                     var day = parseInt(html, 10) || 1;
21610                     var year =  (this.viewDate || new Date()).getUTCFullYear(),
21611                         month = (this.viewDate || new Date()).getUTCMonth();
21612
21613                     if (className.indexOf('old') > -1) {
21614                         if(month === 0 ){
21615                             month = 11;
21616                             year -= 1;
21617                         }else{
21618                             month -= 1;
21619                         }
21620                     } else if (className.indexOf('new') > -1) {
21621                         if (month == 11) {
21622                             month = 0;
21623                             year += 1;
21624                         } else {
21625                             month += 1;
21626                         }
21627                     }
21628                     //Roo.log([year,month,day]);
21629                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21630                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21631 //                    this.fill();
21632                     //Roo.log(this.formatDate(this.date));
21633                     this.setValue(this.formatDate(this.date));
21634                     this.hidePopup();
21635                 }
21636                 break;
21637         }
21638     },
21639     
21640     setStartDate: function(startDate)
21641     {
21642         this.startDate = startDate || -Infinity;
21643         if (this.startDate !== -Infinity) {
21644             this.startDate = this.parseDate(this.startDate);
21645         }
21646         this.update();
21647         this.updateNavArrows();
21648     },
21649
21650     setEndDate: function(endDate)
21651     {
21652         this.endDate = endDate || Infinity;
21653         if (this.endDate !== Infinity) {
21654             this.endDate = this.parseDate(this.endDate);
21655         }
21656         this.update();
21657         this.updateNavArrows();
21658     },
21659     
21660     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21661     {
21662         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21663         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21664             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21665         }
21666         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21667             return parseInt(d, 10);
21668         });
21669         this.update();
21670         this.updateNavArrows();
21671     },
21672     
21673     updateNavArrows: function() 
21674     {
21675         if(this.singleMode){
21676             return;
21677         }
21678         
21679         var d = new Date(this.viewDate),
21680         year = d.getUTCFullYear(),
21681         month = d.getUTCMonth();
21682         
21683         Roo.each(this.picker().select('.prev', true).elements, function(v){
21684             v.show();
21685             switch (this.viewMode) {
21686                 case 0:
21687
21688                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21689                         v.hide();
21690                     }
21691                     break;
21692                 case 1:
21693                 case 2:
21694                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21695                         v.hide();
21696                     }
21697                     break;
21698             }
21699         });
21700         
21701         Roo.each(this.picker().select('.next', true).elements, function(v){
21702             v.show();
21703             switch (this.viewMode) {
21704                 case 0:
21705
21706                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21707                         v.hide();
21708                     }
21709                     break;
21710                 case 1:
21711                 case 2:
21712                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21713                         v.hide();
21714                     }
21715                     break;
21716             }
21717         })
21718     },
21719     
21720     moveMonth: function(date, dir)
21721     {
21722         if (!dir) {
21723             return date;
21724         }
21725         var new_date = new Date(date.valueOf()),
21726         day = new_date.getUTCDate(),
21727         month = new_date.getUTCMonth(),
21728         mag = Math.abs(dir),
21729         new_month, test;
21730         dir = dir > 0 ? 1 : -1;
21731         if (mag == 1){
21732             test = dir == -1
21733             // If going back one month, make sure month is not current month
21734             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21735             ? function(){
21736                 return new_date.getUTCMonth() == month;
21737             }
21738             // If going forward one month, make sure month is as expected
21739             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21740             : function(){
21741                 return new_date.getUTCMonth() != new_month;
21742             };
21743             new_month = month + dir;
21744             new_date.setUTCMonth(new_month);
21745             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21746             if (new_month < 0 || new_month > 11) {
21747                 new_month = (new_month + 12) % 12;
21748             }
21749         } else {
21750             // For magnitudes >1, move one month at a time...
21751             for (var i=0; i<mag; i++) {
21752                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21753                 new_date = this.moveMonth(new_date, dir);
21754             }
21755             // ...then reset the day, keeping it in the new month
21756             new_month = new_date.getUTCMonth();
21757             new_date.setUTCDate(day);
21758             test = function(){
21759                 return new_month != new_date.getUTCMonth();
21760             };
21761         }
21762         // Common date-resetting loop -- if date is beyond end of month, make it
21763         // end of month
21764         while (test()){
21765             new_date.setUTCDate(--day);
21766             new_date.setUTCMonth(new_month);
21767         }
21768         return new_date;
21769     },
21770
21771     moveYear: function(date, dir)
21772     {
21773         return this.moveMonth(date, dir*12);
21774     },
21775
21776     dateWithinRange: function(date)
21777     {
21778         return date >= this.startDate && date <= this.endDate;
21779     },
21780
21781     
21782     remove: function() 
21783     {
21784         this.picker().remove();
21785     },
21786     
21787     validateValue : function(value)
21788     {
21789         if(this.getVisibilityEl().hasClass('hidden')){
21790             return true;
21791         }
21792         
21793         if(value.length < 1)  {
21794             if(this.allowBlank){
21795                 return true;
21796             }
21797             return false;
21798         }
21799         
21800         if(value.length < this.minLength){
21801             return false;
21802         }
21803         if(value.length > this.maxLength){
21804             return false;
21805         }
21806         if(this.vtype){
21807             var vt = Roo.form.VTypes;
21808             if(!vt[this.vtype](value, this)){
21809                 return false;
21810             }
21811         }
21812         if(typeof this.validator == "function"){
21813             var msg = this.validator(value);
21814             if(msg !== true){
21815                 return false;
21816             }
21817         }
21818         
21819         if(this.regex && !this.regex.test(value)){
21820             return false;
21821         }
21822         
21823         if(typeof(this.parseDate(value)) == 'undefined'){
21824             return false;
21825         }
21826         
21827         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21828             return false;
21829         }      
21830         
21831         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21832             return false;
21833         } 
21834         
21835         
21836         return true;
21837     },
21838     
21839     reset : function()
21840     {
21841         this.date = this.viewDate = '';
21842         
21843         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21844     }
21845    
21846 });
21847
21848 Roo.apply(Roo.bootstrap.DateField,  {
21849     
21850     head : {
21851         tag: 'thead',
21852         cn: [
21853         {
21854             tag: 'tr',
21855             cn: [
21856             {
21857                 tag: 'th',
21858                 cls: 'prev',
21859                 html: '<i class="fa fa-arrow-left"/>'
21860             },
21861             {
21862                 tag: 'th',
21863                 cls: 'switch',
21864                 colspan: '5'
21865             },
21866             {
21867                 tag: 'th',
21868                 cls: 'next',
21869                 html: '<i class="fa fa-arrow-right"/>'
21870             }
21871
21872             ]
21873         }
21874         ]
21875     },
21876     
21877     content : {
21878         tag: 'tbody',
21879         cn: [
21880         {
21881             tag: 'tr',
21882             cn: [
21883             {
21884                 tag: 'td',
21885                 colspan: '7'
21886             }
21887             ]
21888         }
21889         ]
21890     },
21891     
21892     footer : {
21893         tag: 'tfoot',
21894         cn: [
21895         {
21896             tag: 'tr',
21897             cn: [
21898             {
21899                 tag: 'th',
21900                 colspan: '7',
21901                 cls: 'today'
21902             }
21903                     
21904             ]
21905         }
21906         ]
21907     },
21908     
21909     dates:{
21910         en: {
21911             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21912             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21913             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21914             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21915             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21916             today: "Today"
21917         }
21918     },
21919     
21920     modes: [
21921     {
21922         clsName: 'days',
21923         navFnc: 'Month',
21924         navStep: 1
21925     },
21926     {
21927         clsName: 'months',
21928         navFnc: 'FullYear',
21929         navStep: 1
21930     },
21931     {
21932         clsName: 'years',
21933         navFnc: 'FullYear',
21934         navStep: 10
21935     }]
21936 });
21937
21938 Roo.apply(Roo.bootstrap.DateField,  {
21939   
21940     template : {
21941         tag: 'div',
21942         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21943         cn: [
21944         {
21945             tag: 'div',
21946             cls: 'datepicker-days',
21947             cn: [
21948             {
21949                 tag: 'table',
21950                 cls: 'table-condensed',
21951                 cn:[
21952                 Roo.bootstrap.DateField.head,
21953                 {
21954                     tag: 'tbody'
21955                 },
21956                 Roo.bootstrap.DateField.footer
21957                 ]
21958             }
21959             ]
21960         },
21961         {
21962             tag: 'div',
21963             cls: 'datepicker-months',
21964             cn: [
21965             {
21966                 tag: 'table',
21967                 cls: 'table-condensed',
21968                 cn:[
21969                 Roo.bootstrap.DateField.head,
21970                 Roo.bootstrap.DateField.content,
21971                 Roo.bootstrap.DateField.footer
21972                 ]
21973             }
21974             ]
21975         },
21976         {
21977             tag: 'div',
21978             cls: 'datepicker-years',
21979             cn: [
21980             {
21981                 tag: 'table',
21982                 cls: 'table-condensed',
21983                 cn:[
21984                 Roo.bootstrap.DateField.head,
21985                 Roo.bootstrap.DateField.content,
21986                 Roo.bootstrap.DateField.footer
21987                 ]
21988             }
21989             ]
21990         }
21991         ]
21992     }
21993 });
21994
21995  
21996
21997  /*
21998  * - LGPL
21999  *
22000  * TimeField
22001  * 
22002  */
22003
22004 /**
22005  * @class Roo.bootstrap.TimeField
22006  * @extends Roo.bootstrap.Input
22007  * Bootstrap DateField class
22008  * 
22009  * 
22010  * @constructor
22011  * Create a new TimeField
22012  * @param {Object} config The config object
22013  */
22014
22015 Roo.bootstrap.TimeField = function(config){
22016     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
22017     this.addEvents({
22018             /**
22019              * @event show
22020              * Fires when this field show.
22021              * @param {Roo.bootstrap.DateField} thisthis
22022              * @param {Mixed} date The date value
22023              */
22024             show : true,
22025             /**
22026              * @event show
22027              * Fires when this field hide.
22028              * @param {Roo.bootstrap.DateField} this
22029              * @param {Mixed} date The date value
22030              */
22031             hide : true,
22032             /**
22033              * @event select
22034              * Fires when select a date.
22035              * @param {Roo.bootstrap.DateField} this
22036              * @param {Mixed} date The date value
22037              */
22038             select : true
22039         });
22040 };
22041
22042 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
22043     
22044     /**
22045      * @cfg {String} format
22046      * The default time format string which can be overriden for localization support.  The format must be
22047      * valid according to {@link Date#parseDate} (defaults to 'H:i').
22048      */
22049     format : "H:i",
22050
22051     getAutoCreate : function()
22052     {
22053         this.after = '<i class="fa far fa-clock"></i>';
22054         return Roo.bootstrap.TimeField.superclass.getAutoCreate.call(this);
22055         
22056          
22057     },
22058     onRender: function(ct, position)
22059     {
22060         
22061         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
22062                 
22063         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.TimeField.template);
22064         
22065         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22066         
22067         this.pop = this.picker().select('>.datepicker-time',true).first();
22068         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22069         
22070         this.picker().on('mousedown', this.onMousedown, this);
22071         this.picker().on('click', this.onClick, this);
22072         
22073         this.picker().addClass('datepicker-dropdown');
22074     
22075         this.fillTime();
22076         this.update();
22077             
22078         this.pop.select('.hours-up', true).first().on('click', this.onIncrementHours, this);
22079         this.pop.select('.hours-down', true).first().on('click', this.onDecrementHours, this);
22080         this.pop.select('.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
22081         this.pop.select('.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
22082         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
22083         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
22084
22085     },
22086     
22087     fireKey: function(e){
22088         if (!this.picker().isVisible()){
22089             if (e.keyCode == 27) { // allow escape to hide and re-show picker
22090                 this.show();
22091             }
22092             return;
22093         }
22094
22095         e.preventDefault();
22096         
22097         switch(e.keyCode){
22098             case 27: // escape
22099                 this.hide();
22100                 break;
22101             case 37: // left
22102             case 39: // right
22103                 this.onTogglePeriod();
22104                 break;
22105             case 38: // up
22106                 this.onIncrementMinutes();
22107                 break;
22108             case 40: // down
22109                 this.onDecrementMinutes();
22110                 break;
22111             case 13: // enter
22112             case 9: // tab
22113                 this.setTime();
22114                 break;
22115         }
22116     },
22117     
22118     onClick: function(e) {
22119         e.stopPropagation();
22120         e.preventDefault();
22121     },
22122     
22123     picker : function()
22124     {
22125         return this.pickerEl;
22126     },
22127     
22128     fillTime: function()
22129     {    
22130         var time = this.pop.select('tbody', true).first();
22131         
22132         time.dom.innerHTML = '';
22133         
22134         time.createChild({
22135             tag: 'tr',
22136             cn: [
22137                 {
22138                     tag: 'td',
22139                     cn: [
22140                         {
22141                             tag: 'a',
22142                             href: '#',
22143                             cls: 'btn',
22144                             cn: [
22145                                 {
22146                                     tag: 'i',
22147                                     cls: 'hours-up fa fas fa-chevron-up'
22148                                 }
22149                             ]
22150                         } 
22151                     ]
22152                 },
22153                 {
22154                     tag: 'td',
22155                     cls: 'separator'
22156                 },
22157                 {
22158                     tag: 'td',
22159                     cn: [
22160                         {
22161                             tag: 'a',
22162                             href: '#',
22163                             cls: 'btn',
22164                             cn: [
22165                                 {
22166                                     tag: 'i',
22167                                     cls: 'minutes-up fa fas fa-chevron-up'
22168                                 }
22169                             ]
22170                         }
22171                     ]
22172                 },
22173                 {
22174                     tag: 'td',
22175                     cls: 'separator'
22176                 }
22177             ]
22178         });
22179         
22180         time.createChild({
22181             tag: 'tr',
22182             cn: [
22183                 {
22184                     tag: 'td',
22185                     cn: [
22186                         {
22187                             tag: 'span',
22188                             cls: 'timepicker-hour',
22189                             html: '00'
22190                         }  
22191                     ]
22192                 },
22193                 {
22194                     tag: 'td',
22195                     cls: 'separator',
22196                     html: ':'
22197                 },
22198                 {
22199                     tag: 'td',
22200                     cn: [
22201                         {
22202                             tag: 'span',
22203                             cls: 'timepicker-minute',
22204                             html: '00'
22205                         }  
22206                     ]
22207                 },
22208                 {
22209                     tag: 'td',
22210                     cls: 'separator'
22211                 },
22212                 {
22213                     tag: 'td',
22214                     cn: [
22215                         {
22216                             tag: 'button',
22217                             type: 'button',
22218                             cls: 'btn btn-primary period',
22219                             html: 'AM'
22220                             
22221                         }
22222                     ]
22223                 }
22224             ]
22225         });
22226         
22227         time.createChild({
22228             tag: 'tr',
22229             cn: [
22230                 {
22231                     tag: 'td',
22232                     cn: [
22233                         {
22234                             tag: 'a',
22235                             href: '#',
22236                             cls: 'btn',
22237                             cn: [
22238                                 {
22239                                     tag: 'span',
22240                                     cls: 'hours-down fa fas fa-chevron-down'
22241                                 }
22242                             ]
22243                         }
22244                     ]
22245                 },
22246                 {
22247                     tag: 'td',
22248                     cls: 'separator'
22249                 },
22250                 {
22251                     tag: 'td',
22252                     cn: [
22253                         {
22254                             tag: 'a',
22255                             href: '#',
22256                             cls: 'btn',
22257                             cn: [
22258                                 {
22259                                     tag: 'span',
22260                                     cls: 'minutes-down fa fas fa-chevron-down'
22261                                 }
22262                             ]
22263                         }
22264                     ]
22265                 },
22266                 {
22267                     tag: 'td',
22268                     cls: 'separator'
22269                 }
22270             ]
22271         });
22272         
22273     },
22274     
22275     update: function()
22276     {
22277         
22278         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22279         
22280         this.fill();
22281     },
22282     
22283     fill: function() 
22284     {
22285         var hours = this.time.getHours();
22286         var minutes = this.time.getMinutes();
22287         var period = 'AM';
22288         
22289         if(hours > 11){
22290             period = 'PM';
22291         }
22292         
22293         if(hours == 0){
22294             hours = 12;
22295         }
22296         
22297         
22298         if(hours > 12){
22299             hours = hours - 12;
22300         }
22301         
22302         if(hours < 10){
22303             hours = '0' + hours;
22304         }
22305         
22306         if(minutes < 10){
22307             minutes = '0' + minutes;
22308         }
22309         
22310         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22311         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22312         this.pop.select('button', true).first().dom.innerHTML = period;
22313         
22314     },
22315     
22316     place: function()
22317     {   
22318         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22319         
22320         var cls = ['bottom'];
22321         
22322         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22323             cls.pop();
22324             cls.push('top');
22325         }
22326         
22327         cls.push('right');
22328         
22329         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22330             cls.pop();
22331             cls.push('left');
22332         }
22333         //this.picker().setXY(20000,20000);
22334         this.picker().addClass(cls.join('-'));
22335         
22336         var _this = this;
22337         
22338         Roo.each(cls, function(c){
22339             if(c == 'bottom'){
22340                 (function() {
22341                  //  
22342                 }).defer(200);
22343                  _this.picker().alignTo(_this.inputEl(),   "tr-br", [0, 10], false);
22344                 //_this.picker().setTop(_this.inputEl().getHeight());
22345                 return;
22346             }
22347             if(c == 'top'){
22348                  _this.picker().alignTo(_this.inputEl(),   "br-tr", [0, 10], false);
22349                 
22350                 //_this.picker().setTop(0 - _this.picker().getHeight());
22351                 return;
22352             }
22353             /*
22354             if(c == 'left'){
22355                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22356                 return;
22357             }
22358             if(c == 'right'){
22359                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22360                 return;
22361             }
22362             */
22363         });
22364         
22365     },
22366   
22367     onFocus : function()
22368     {
22369         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22370         this.show();
22371     },
22372     
22373     onBlur : function()
22374     {
22375         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22376         this.hide();
22377     },
22378     
22379     show : function()
22380     {
22381         this.picker().show();
22382         this.pop.show();
22383         this.update();
22384         this.place();
22385         
22386         this.fireEvent('show', this, this.date);
22387     },
22388     
22389     hide : function()
22390     {
22391         this.picker().hide();
22392         this.pop.hide();
22393         
22394         this.fireEvent('hide', this, this.date);
22395     },
22396     
22397     setTime : function()
22398     {
22399         this.hide();
22400         this.setValue(this.time.format(this.format));
22401         
22402         this.fireEvent('select', this, this.date);
22403         
22404         
22405     },
22406     
22407     onMousedown: function(e){
22408         e.stopPropagation();
22409         e.preventDefault();
22410     },
22411     
22412     onIncrementHours: function()
22413     {
22414         Roo.log('onIncrementHours');
22415         this.time = this.time.add(Date.HOUR, 1);
22416         this.update();
22417         
22418     },
22419     
22420     onDecrementHours: function()
22421     {
22422         Roo.log('onDecrementHours');
22423         this.time = this.time.add(Date.HOUR, -1);
22424         this.update();
22425     },
22426     
22427     onIncrementMinutes: function()
22428     {
22429         Roo.log('onIncrementMinutes');
22430         this.time = this.time.add(Date.MINUTE, 1);
22431         this.update();
22432     },
22433     
22434     onDecrementMinutes: function()
22435     {
22436         Roo.log('onDecrementMinutes');
22437         this.time = this.time.add(Date.MINUTE, -1);
22438         this.update();
22439     },
22440     
22441     onTogglePeriod: function()
22442     {
22443         Roo.log('onTogglePeriod');
22444         this.time = this.time.add(Date.HOUR, 12);
22445         this.update();
22446     }
22447     
22448    
22449 });
22450  
22451
22452 Roo.apply(Roo.bootstrap.TimeField,  {
22453   
22454     template : {
22455         tag: 'div',
22456         cls: 'datepicker dropdown-menu',
22457         cn: [
22458             {
22459                 tag: 'div',
22460                 cls: 'datepicker-time',
22461                 cn: [
22462                 {
22463                     tag: 'table',
22464                     cls: 'table-condensed',
22465                     cn:[
22466                         {
22467                             tag: 'tbody',
22468                             cn: [
22469                                 {
22470                                     tag: 'tr',
22471                                     cn: [
22472                                     {
22473                                         tag: 'td',
22474                                         colspan: '7'
22475                                     }
22476                                     ]
22477                                 }
22478                             ]
22479                         },
22480                         {
22481                             tag: 'tfoot',
22482                             cn: [
22483                                 {
22484                                     tag: 'tr',
22485                                     cn: [
22486                                     {
22487                                         tag: 'th',
22488                                         colspan: '7',
22489                                         cls: '',
22490                                         cn: [
22491                                             {
22492                                                 tag: 'button',
22493                                                 cls: 'btn btn-info ok',
22494                                                 html: 'OK'
22495                                             }
22496                                         ]
22497                                     }
22498                     
22499                                     ]
22500                                 }
22501                             ]
22502                         }
22503                     ]
22504                 }
22505                 ]
22506             }
22507         ]
22508     }
22509 });
22510
22511  
22512
22513  /*
22514  * - LGPL
22515  *
22516  * MonthField
22517  * 
22518  */
22519
22520 /**
22521  * @class Roo.bootstrap.MonthField
22522  * @extends Roo.bootstrap.Input
22523  * Bootstrap MonthField class
22524  * 
22525  * @cfg {String} language default en
22526  * 
22527  * @constructor
22528  * Create a new MonthField
22529  * @param {Object} config The config object
22530  */
22531
22532 Roo.bootstrap.MonthField = function(config){
22533     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22534     
22535     this.addEvents({
22536         /**
22537          * @event show
22538          * Fires when this field show.
22539          * @param {Roo.bootstrap.MonthField} this
22540          * @param {Mixed} date The date value
22541          */
22542         show : true,
22543         /**
22544          * @event show
22545          * Fires when this field hide.
22546          * @param {Roo.bootstrap.MonthField} this
22547          * @param {Mixed} date The date value
22548          */
22549         hide : true,
22550         /**
22551          * @event select
22552          * Fires when select a date.
22553          * @param {Roo.bootstrap.MonthField} this
22554          * @param {String} oldvalue The old value
22555          * @param {String} newvalue The new value
22556          */
22557         select : true
22558     });
22559 };
22560
22561 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22562     
22563     onRender: function(ct, position)
22564     {
22565         
22566         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22567         
22568         this.language = this.language || 'en';
22569         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22570         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22571         
22572         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22573         this.isInline = false;
22574         this.isInput = true;
22575         this.component = this.el.select('.add-on', true).first() || false;
22576         this.component = (this.component && this.component.length === 0) ? false : this.component;
22577         this.hasInput = this.component && this.inputEL().length;
22578         
22579         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22580         
22581         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22582         
22583         this.picker().on('mousedown', this.onMousedown, this);
22584         this.picker().on('click', this.onClick, this);
22585         
22586         this.picker().addClass('datepicker-dropdown');
22587         
22588         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22589             v.setStyle('width', '189px');
22590         });
22591         
22592         this.fillMonths();
22593         
22594         this.update();
22595         
22596         if(this.isInline) {
22597             this.show();
22598         }
22599         
22600     },
22601     
22602     setValue: function(v, suppressEvent)
22603     {   
22604         var o = this.getValue();
22605         
22606         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22607         
22608         this.update();
22609
22610         if(suppressEvent !== true){
22611             this.fireEvent('select', this, o, v);
22612         }
22613         
22614     },
22615     
22616     getValue: function()
22617     {
22618         return this.value;
22619     },
22620     
22621     onClick: function(e) 
22622     {
22623         e.stopPropagation();
22624         e.preventDefault();
22625         
22626         var target = e.getTarget();
22627         
22628         if(target.nodeName.toLowerCase() === 'i'){
22629             target = Roo.get(target).dom.parentNode;
22630         }
22631         
22632         var nodeName = target.nodeName;
22633         var className = target.className;
22634         var html = target.innerHTML;
22635         
22636         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22637             return;
22638         }
22639         
22640         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22641         
22642         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22643         
22644         this.hide();
22645                         
22646     },
22647     
22648     picker : function()
22649     {
22650         return this.pickerEl;
22651     },
22652     
22653     fillMonths: function()
22654     {    
22655         var i = 0;
22656         var months = this.picker().select('>.datepicker-months td', true).first();
22657         
22658         months.dom.innerHTML = '';
22659         
22660         while (i < 12) {
22661             var month = {
22662                 tag: 'span',
22663                 cls: 'month',
22664                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22665             };
22666             
22667             months.createChild(month);
22668         }
22669         
22670     },
22671     
22672     update: function()
22673     {
22674         var _this = this;
22675         
22676         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22677             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22678         }
22679         
22680         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22681             e.removeClass('active');
22682             
22683             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22684                 e.addClass('active');
22685             }
22686         })
22687     },
22688     
22689     place: function()
22690     {
22691         if(this.isInline) {
22692             return;
22693         }
22694         
22695         this.picker().removeClass(['bottom', 'top']);
22696         
22697         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22698             /*
22699              * place to the top of element!
22700              *
22701              */
22702             
22703             this.picker().addClass('top');
22704             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22705             
22706             return;
22707         }
22708         
22709         this.picker().addClass('bottom');
22710         
22711         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22712     },
22713     
22714     onFocus : function()
22715     {
22716         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22717         this.show();
22718     },
22719     
22720     onBlur : function()
22721     {
22722         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22723         
22724         var d = this.inputEl().getValue();
22725         
22726         this.setValue(d);
22727                 
22728         this.hide();
22729     },
22730     
22731     show : function()
22732     {
22733         this.picker().show();
22734         this.picker().select('>.datepicker-months', true).first().show();
22735         this.update();
22736         this.place();
22737         
22738         this.fireEvent('show', this, this.date);
22739     },
22740     
22741     hide : function()
22742     {
22743         if(this.isInline) {
22744             return;
22745         }
22746         this.picker().hide();
22747         this.fireEvent('hide', this, this.date);
22748         
22749     },
22750     
22751     onMousedown: function(e)
22752     {
22753         e.stopPropagation();
22754         e.preventDefault();
22755     },
22756     
22757     keyup: function(e)
22758     {
22759         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22760         this.update();
22761     },
22762
22763     fireKey: function(e)
22764     {
22765         if (!this.picker().isVisible()){
22766             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22767                 this.show();
22768             }
22769             return;
22770         }
22771         
22772         var dir;
22773         
22774         switch(e.keyCode){
22775             case 27: // escape
22776                 this.hide();
22777                 e.preventDefault();
22778                 break;
22779             case 37: // left
22780             case 39: // right
22781                 dir = e.keyCode == 37 ? -1 : 1;
22782                 
22783                 this.vIndex = this.vIndex + dir;
22784                 
22785                 if(this.vIndex < 0){
22786                     this.vIndex = 0;
22787                 }
22788                 
22789                 if(this.vIndex > 11){
22790                     this.vIndex = 11;
22791                 }
22792                 
22793                 if(isNaN(this.vIndex)){
22794                     this.vIndex = 0;
22795                 }
22796                 
22797                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22798                 
22799                 break;
22800             case 38: // up
22801             case 40: // down
22802                 
22803                 dir = e.keyCode == 38 ? -1 : 1;
22804                 
22805                 this.vIndex = this.vIndex + dir * 4;
22806                 
22807                 if(this.vIndex < 0){
22808                     this.vIndex = 0;
22809                 }
22810                 
22811                 if(this.vIndex > 11){
22812                     this.vIndex = 11;
22813                 }
22814                 
22815                 if(isNaN(this.vIndex)){
22816                     this.vIndex = 0;
22817                 }
22818                 
22819                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22820                 break;
22821                 
22822             case 13: // enter
22823                 
22824                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22825                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22826                 }
22827                 
22828                 this.hide();
22829                 e.preventDefault();
22830                 break;
22831             case 9: // tab
22832                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22833                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22834                 }
22835                 this.hide();
22836                 break;
22837             case 16: // shift
22838             case 17: // ctrl
22839             case 18: // alt
22840                 break;
22841             default :
22842                 this.hide();
22843                 
22844         }
22845     },
22846     
22847     remove: function() 
22848     {
22849         this.picker().remove();
22850     }
22851    
22852 });
22853
22854 Roo.apply(Roo.bootstrap.MonthField,  {
22855     
22856     content : {
22857         tag: 'tbody',
22858         cn: [
22859         {
22860             tag: 'tr',
22861             cn: [
22862             {
22863                 tag: 'td',
22864                 colspan: '7'
22865             }
22866             ]
22867         }
22868         ]
22869     },
22870     
22871     dates:{
22872         en: {
22873             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22874             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22875         }
22876     }
22877 });
22878
22879 Roo.apply(Roo.bootstrap.MonthField,  {
22880   
22881     template : {
22882         tag: 'div',
22883         cls: 'datepicker dropdown-menu roo-dynamic',
22884         cn: [
22885             {
22886                 tag: 'div',
22887                 cls: 'datepicker-months',
22888                 cn: [
22889                 {
22890                     tag: 'table',
22891                     cls: 'table-condensed',
22892                     cn:[
22893                         Roo.bootstrap.DateField.content
22894                     ]
22895                 }
22896                 ]
22897             }
22898         ]
22899     }
22900 });
22901
22902  
22903
22904  
22905  /*
22906  * - LGPL
22907  *
22908  * CheckBox
22909  * 
22910  */
22911
22912 /**
22913  * @class Roo.bootstrap.CheckBox
22914  * @extends Roo.bootstrap.Input
22915  * Bootstrap CheckBox class
22916  * 
22917  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22918  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22919  * @cfg {String} boxLabel The text that appears beside the checkbox
22920  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22921  * @cfg {Boolean} checked initnal the element
22922  * @cfg {Boolean} inline inline the element (default false)
22923  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22924  * @cfg {String} tooltip label tooltip
22925  * 
22926  * @constructor
22927  * Create a new CheckBox
22928  * @param {Object} config The config object
22929  */
22930
22931 Roo.bootstrap.CheckBox = function(config){
22932     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22933    
22934     this.addEvents({
22935         /**
22936         * @event check
22937         * Fires when the element is checked or unchecked.
22938         * @param {Roo.bootstrap.CheckBox} this This input
22939         * @param {Boolean} checked The new checked value
22940         */
22941        check : true,
22942        /**
22943         * @event click
22944         * Fires when the element is click.
22945         * @param {Roo.bootstrap.CheckBox} this This input
22946         */
22947        click : true
22948     });
22949     
22950 };
22951
22952 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22953   
22954     inputType: 'checkbox',
22955     inputValue: 1,
22956     valueOff: 0,
22957     boxLabel: false,
22958     checked: false,
22959     weight : false,
22960     inline: false,
22961     tooltip : '',
22962     
22963     // checkbox success does not make any sense really.. 
22964     invalidClass : "",
22965     validClass : "",
22966     
22967     
22968     getAutoCreate : function()
22969     {
22970         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22971         
22972         var id = Roo.id();
22973         
22974         var cfg = {};
22975         
22976         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22977         
22978         if(this.inline){
22979             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22980         }
22981         
22982         var input =  {
22983             tag: 'input',
22984             id : id,
22985             type : this.inputType,
22986             value : this.inputValue,
22987             cls : 'roo-' + this.inputType, //'form-box',
22988             placeholder : this.placeholder || ''
22989             
22990         };
22991         
22992         if(this.inputType != 'radio'){
22993             var hidden =  {
22994                 tag: 'input',
22995                 type : 'hidden',
22996                 cls : 'roo-hidden-value',
22997                 value : this.checked ? this.inputValue : this.valueOff
22998             };
22999         }
23000         
23001             
23002         if (this.weight) { // Validity check?
23003             cfg.cls += " " + this.inputType + "-" + this.weight;
23004         }
23005         
23006         if (this.disabled) {
23007             input.disabled=true;
23008         }
23009         
23010         if(this.checked){
23011             input.checked = this.checked;
23012         }
23013         
23014         if (this.name) {
23015             
23016             input.name = this.name;
23017             
23018             if(this.inputType != 'radio'){
23019                 hidden.name = this.name;
23020                 input.name = '_hidden_' + this.name;
23021             }
23022         }
23023         
23024         if (this.size) {
23025             input.cls += ' input-' + this.size;
23026         }
23027         
23028         var settings=this;
23029         
23030         ['xs','sm','md','lg'].map(function(size){
23031             if (settings[size]) {
23032                 cfg.cls += ' col-' + size + '-' + settings[size];
23033             }
23034         });
23035         
23036         var inputblock = input;
23037          
23038         if (this.before || this.after) {
23039             
23040             inputblock = {
23041                 cls : 'input-group',
23042                 cn :  [] 
23043             };
23044             
23045             if (this.before) {
23046                 inputblock.cn.push({
23047                     tag :'span',
23048                     cls : 'input-group-addon',
23049                     html : this.before
23050                 });
23051             }
23052             
23053             inputblock.cn.push(input);
23054             
23055             if(this.inputType != 'radio'){
23056                 inputblock.cn.push(hidden);
23057             }
23058             
23059             if (this.after) {
23060                 inputblock.cn.push({
23061                     tag :'span',
23062                     cls : 'input-group-addon',
23063                     html : this.after
23064                 });
23065             }
23066             
23067         }
23068         var boxLabelCfg = false;
23069         
23070         if(this.boxLabel){
23071            
23072             boxLabelCfg = {
23073                 tag: 'label',
23074                 //'for': id, // box label is handled by onclick - so no for...
23075                 cls: 'box-label',
23076                 html: this.boxLabel
23077             };
23078             if(this.tooltip){
23079                 boxLabelCfg.tooltip = this.tooltip;
23080             }
23081              
23082         }
23083         
23084         
23085         if (align ==='left' && this.fieldLabel.length) {
23086 //                Roo.log("left and has label");
23087             cfg.cn = [
23088                 {
23089                     tag: 'label',
23090                     'for' :  id,
23091                     cls : 'control-label',
23092                     html : this.fieldLabel
23093                 },
23094                 {
23095                     cls : "", 
23096                     cn: [
23097                         inputblock
23098                     ]
23099                 }
23100             ];
23101             
23102             if (boxLabelCfg) {
23103                 cfg.cn[1].cn.push(boxLabelCfg);
23104             }
23105             
23106             if(this.labelWidth > 12){
23107                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
23108             }
23109             
23110             if(this.labelWidth < 13 && this.labelmd == 0){
23111                 this.labelmd = this.labelWidth;
23112             }
23113             
23114             if(this.labellg > 0){
23115                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
23116                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
23117             }
23118             
23119             if(this.labelmd > 0){
23120                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
23121                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
23122             }
23123             
23124             if(this.labelsm > 0){
23125                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
23126                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
23127             }
23128             
23129             if(this.labelxs > 0){
23130                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
23131                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
23132             }
23133             
23134         } else if ( this.fieldLabel.length) {
23135 //                Roo.log(" label");
23136                 cfg.cn = [
23137                    
23138                     {
23139                         tag: this.boxLabel ? 'span' : 'label',
23140                         'for': id,
23141                         cls: 'control-label box-input-label',
23142                         //cls : 'input-group-addon',
23143                         html : this.fieldLabel
23144                     },
23145                     
23146                     inputblock
23147                     
23148                 ];
23149                 if (boxLabelCfg) {
23150                     cfg.cn.push(boxLabelCfg);
23151                 }
23152
23153         } else {
23154             
23155 //                Roo.log(" no label && no align");
23156                 cfg.cn = [  inputblock ] ;
23157                 if (boxLabelCfg) {
23158                     cfg.cn.push(boxLabelCfg);
23159                 }
23160
23161                 
23162         }
23163         
23164        
23165         
23166         if(this.inputType != 'radio'){
23167             cfg.cn.push(hidden);
23168         }
23169         
23170         return cfg;
23171         
23172     },
23173     
23174     /**
23175      * return the real input element.
23176      */
23177     inputEl: function ()
23178     {
23179         return this.el.select('input.roo-' + this.inputType,true).first();
23180     },
23181     hiddenEl: function ()
23182     {
23183         return this.el.select('input.roo-hidden-value',true).first();
23184     },
23185     
23186     labelEl: function()
23187     {
23188         return this.el.select('label.control-label',true).first();
23189     },
23190     /* depricated... */
23191     
23192     label: function()
23193     {
23194         return this.labelEl();
23195     },
23196     
23197     boxLabelEl: function()
23198     {
23199         return this.el.select('label.box-label',true).first();
23200     },
23201     
23202     initEvents : function()
23203     {
23204 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23205         
23206         this.inputEl().on('click', this.onClick,  this);
23207         
23208         if (this.boxLabel) { 
23209             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23210         }
23211         
23212         this.startValue = this.getValue();
23213         
23214         if(this.groupId){
23215             Roo.bootstrap.CheckBox.register(this);
23216         }
23217     },
23218     
23219     onClick : function(e)
23220     {   
23221         if(this.fireEvent('click', this, e) !== false){
23222             this.setChecked(!this.checked);
23223         }
23224         
23225     },
23226     
23227     setChecked : function(state,suppressEvent)
23228     {
23229         this.startValue = this.getValue();
23230
23231         if(this.inputType == 'radio'){
23232             
23233             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23234                 e.dom.checked = false;
23235             });
23236             
23237             this.inputEl().dom.checked = true;
23238             
23239             this.inputEl().dom.value = this.inputValue;
23240             
23241             if(suppressEvent !== true){
23242                 this.fireEvent('check', this, true);
23243             }
23244             
23245             this.validate();
23246             
23247             return;
23248         }
23249         
23250         this.checked = state;
23251         
23252         this.inputEl().dom.checked = state;
23253         
23254         
23255         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23256         
23257         if(suppressEvent !== true){
23258             this.fireEvent('check', this, state);
23259         }
23260         
23261         this.validate();
23262     },
23263     
23264     getValue : function()
23265     {
23266         if(this.inputType == 'radio'){
23267             return this.getGroupValue();
23268         }
23269         
23270         return this.hiddenEl().dom.value;
23271         
23272     },
23273     
23274     getGroupValue : function()
23275     {
23276         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23277             return '';
23278         }
23279         
23280         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23281     },
23282     
23283     setValue : function(v,suppressEvent)
23284     {
23285         if(this.inputType == 'radio'){
23286             this.setGroupValue(v, suppressEvent);
23287             return;
23288         }
23289         
23290         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23291         
23292         this.validate();
23293     },
23294     
23295     setGroupValue : function(v, suppressEvent)
23296     {
23297         this.startValue = this.getValue();
23298         
23299         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23300             e.dom.checked = false;
23301             
23302             if(e.dom.value == v){
23303                 e.dom.checked = true;
23304             }
23305         });
23306         
23307         if(suppressEvent !== true){
23308             this.fireEvent('check', this, true);
23309         }
23310
23311         this.validate();
23312         
23313         return;
23314     },
23315     
23316     validate : function()
23317     {
23318         if(this.getVisibilityEl().hasClass('hidden')){
23319             return true;
23320         }
23321         
23322         if(
23323                 this.disabled || 
23324                 (this.inputType == 'radio' && this.validateRadio()) ||
23325                 (this.inputType == 'checkbox' && this.validateCheckbox())
23326         ){
23327             this.markValid();
23328             return true;
23329         }
23330         
23331         this.markInvalid();
23332         return false;
23333     },
23334     
23335     validateRadio : function()
23336     {
23337         if(this.getVisibilityEl().hasClass('hidden')){
23338             return true;
23339         }
23340         
23341         if(this.allowBlank){
23342             return true;
23343         }
23344         
23345         var valid = false;
23346         
23347         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23348             if(!e.dom.checked){
23349                 return;
23350             }
23351             
23352             valid = true;
23353             
23354             return false;
23355         });
23356         
23357         return valid;
23358     },
23359     
23360     validateCheckbox : function()
23361     {
23362         if(!this.groupId){
23363             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23364             //return (this.getValue() == this.inputValue) ? true : false;
23365         }
23366         
23367         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23368         
23369         if(!group){
23370             return false;
23371         }
23372         
23373         var r = false;
23374         
23375         for(var i in group){
23376             if(group[i].el.isVisible(true)){
23377                 r = false;
23378                 break;
23379             }
23380             
23381             r = true;
23382         }
23383         
23384         for(var i in group){
23385             if(r){
23386                 break;
23387             }
23388             
23389             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23390         }
23391         
23392         return r;
23393     },
23394     
23395     /**
23396      * Mark this field as valid
23397      */
23398     markValid : function()
23399     {
23400         var _this = this;
23401         
23402         this.fireEvent('valid', this);
23403         
23404         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23405         
23406         if(this.groupId){
23407             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23408         }
23409         
23410         if(label){
23411             label.markValid();
23412         }
23413
23414         if(this.inputType == 'radio'){
23415             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23416                 var fg = e.findParent('.form-group', false, true);
23417                 if (Roo.bootstrap.version == 3) {
23418                     fg.removeClass([_this.invalidClass, _this.validClass]);
23419                     fg.addClass(_this.validClass);
23420                 } else {
23421                     fg.removeClass(['is-valid', 'is-invalid']);
23422                     fg.addClass('is-valid');
23423                 }
23424             });
23425             
23426             return;
23427         }
23428
23429         if(!this.groupId){
23430             var fg = this.el.findParent('.form-group', false, true);
23431             if (Roo.bootstrap.version == 3) {
23432                 fg.removeClass([this.invalidClass, this.validClass]);
23433                 fg.addClass(this.validClass);
23434             } else {
23435                 fg.removeClass(['is-valid', 'is-invalid']);
23436                 fg.addClass('is-valid');
23437             }
23438             return;
23439         }
23440         
23441         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23442         
23443         if(!group){
23444             return;
23445         }
23446         
23447         for(var i in group){
23448             var fg = group[i].el.findParent('.form-group', false, true);
23449             if (Roo.bootstrap.version == 3) {
23450                 fg.removeClass([this.invalidClass, this.validClass]);
23451                 fg.addClass(this.validClass);
23452             } else {
23453                 fg.removeClass(['is-valid', 'is-invalid']);
23454                 fg.addClass('is-valid');
23455             }
23456         }
23457     },
23458     
23459      /**
23460      * Mark this field as invalid
23461      * @param {String} msg The validation message
23462      */
23463     markInvalid : function(msg)
23464     {
23465         if(this.allowBlank){
23466             return;
23467         }
23468         
23469         var _this = this;
23470         
23471         this.fireEvent('invalid', this, msg);
23472         
23473         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23474         
23475         if(this.groupId){
23476             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23477         }
23478         
23479         if(label){
23480             label.markInvalid();
23481         }
23482             
23483         if(this.inputType == 'radio'){
23484             
23485             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23486                 var fg = e.findParent('.form-group', false, true);
23487                 if (Roo.bootstrap.version == 3) {
23488                     fg.removeClass([_this.invalidClass, _this.validClass]);
23489                     fg.addClass(_this.invalidClass);
23490                 } else {
23491                     fg.removeClass(['is-invalid', 'is-valid']);
23492                     fg.addClass('is-invalid');
23493                 }
23494             });
23495             
23496             return;
23497         }
23498         
23499         if(!this.groupId){
23500             var fg = this.el.findParent('.form-group', false, true);
23501             if (Roo.bootstrap.version == 3) {
23502                 fg.removeClass([_this.invalidClass, _this.validClass]);
23503                 fg.addClass(_this.invalidClass);
23504             } else {
23505                 fg.removeClass(['is-invalid', 'is-valid']);
23506                 fg.addClass('is-invalid');
23507             }
23508             return;
23509         }
23510         
23511         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23512         
23513         if(!group){
23514             return;
23515         }
23516         
23517         for(var i in group){
23518             var fg = group[i].el.findParent('.form-group', false, true);
23519             if (Roo.bootstrap.version == 3) {
23520                 fg.removeClass([_this.invalidClass, _this.validClass]);
23521                 fg.addClass(_this.invalidClass);
23522             } else {
23523                 fg.removeClass(['is-invalid', 'is-valid']);
23524                 fg.addClass('is-invalid');
23525             }
23526         }
23527         
23528     },
23529     
23530     clearInvalid : function()
23531     {
23532         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23533         
23534         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23535         
23536         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23537         
23538         if (label && label.iconEl) {
23539             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23540             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23541         }
23542     },
23543     
23544     disable : function()
23545     {
23546         if(this.inputType != 'radio'){
23547             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23548             return;
23549         }
23550         
23551         var _this = this;
23552         
23553         if(this.rendered){
23554             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23555                 _this.getActionEl().addClass(this.disabledClass);
23556                 e.dom.disabled = true;
23557             });
23558         }
23559         
23560         this.disabled = true;
23561         this.fireEvent("disable", this);
23562         return this;
23563     },
23564
23565     enable : function()
23566     {
23567         if(this.inputType != 'radio'){
23568             Roo.bootstrap.CheckBox.superclass.enable.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().removeClass(this.disabledClass);
23577                 e.dom.disabled = false;
23578             });
23579         }
23580         
23581         this.disabled = false;
23582         this.fireEvent("enable", this);
23583         return this;
23584     },
23585     
23586     setBoxLabel : function(v)
23587     {
23588         this.boxLabel = v;
23589         
23590         if(this.rendered){
23591             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23592         }
23593     }
23594
23595 });
23596
23597 Roo.apply(Roo.bootstrap.CheckBox, {
23598     
23599     groups: {},
23600     
23601      /**
23602     * register a CheckBox Group
23603     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23604     */
23605     register : function(checkbox)
23606     {
23607         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23608             this.groups[checkbox.groupId] = {};
23609         }
23610         
23611         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23612             return;
23613         }
23614         
23615         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23616         
23617     },
23618     /**
23619     * fetch a CheckBox Group based on the group ID
23620     * @param {string} the group ID
23621     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23622     */
23623     get: function(groupId) {
23624         if (typeof(this.groups[groupId]) == 'undefined') {
23625             return false;
23626         }
23627         
23628         return this.groups[groupId] ;
23629     }
23630     
23631     
23632 });
23633 /*
23634  * - LGPL
23635  *
23636  * RadioItem
23637  * 
23638  */
23639
23640 /**
23641  * @class Roo.bootstrap.Radio
23642  * @extends Roo.bootstrap.Component
23643  * Bootstrap Radio class
23644  * @cfg {String} boxLabel - the label associated
23645  * @cfg {String} value - the value of radio
23646  * 
23647  * @constructor
23648  * Create a new Radio
23649  * @param {Object} config The config object
23650  */
23651 Roo.bootstrap.Radio = function(config){
23652     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23653     
23654 };
23655
23656 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23657     
23658     boxLabel : '',
23659     
23660     value : '',
23661     
23662     getAutoCreate : function()
23663     {
23664         var cfg = {
23665             tag : 'div',
23666             cls : 'form-group radio',
23667             cn : [
23668                 {
23669                     tag : 'label',
23670                     cls : 'box-label',
23671                     html : this.boxLabel
23672                 }
23673             ]
23674         };
23675         
23676         return cfg;
23677     },
23678     
23679     initEvents : function() 
23680     {
23681         this.parent().register(this);
23682         
23683         this.el.on('click', this.onClick, this);
23684         
23685     },
23686     
23687     onClick : function(e)
23688     {
23689         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23690             this.setChecked(true);
23691         }
23692     },
23693     
23694     setChecked : function(state, suppressEvent)
23695     {
23696         this.parent().setValue(this.value, suppressEvent);
23697         
23698     },
23699     
23700     setBoxLabel : function(v)
23701     {
23702         this.boxLabel = v;
23703         
23704         if(this.rendered){
23705             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23706         }
23707     }
23708     
23709 });
23710  
23711
23712  /*
23713  * - LGPL
23714  *
23715  * Input
23716  * 
23717  */
23718
23719 /**
23720  * @class Roo.bootstrap.SecurePass
23721  * @extends Roo.bootstrap.Input
23722  * Bootstrap SecurePass class
23723  *
23724  * 
23725  * @constructor
23726  * Create a new SecurePass
23727  * @param {Object} config The config object
23728  */
23729  
23730 Roo.bootstrap.SecurePass = function (config) {
23731     // these go here, so the translation tool can replace them..
23732     this.errors = {
23733         PwdEmpty: "Please type a password, and then retype it to confirm.",
23734         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23735         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23736         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23737         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23738         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23739         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23740         TooWeak: "Your password is Too Weak."
23741     },
23742     this.meterLabel = "Password strength:";
23743     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23744     this.meterClass = [
23745         "roo-password-meter-tooweak", 
23746         "roo-password-meter-weak", 
23747         "roo-password-meter-medium", 
23748         "roo-password-meter-strong", 
23749         "roo-password-meter-grey"
23750     ];
23751     
23752     this.errors = {};
23753     
23754     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23755 }
23756
23757 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23758     /**
23759      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23760      * {
23761      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23762      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23763      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23764      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23765      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23766      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23767      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23768      * })
23769      */
23770     // private
23771     
23772     meterWidth: 300,
23773     errorMsg :'',    
23774     errors: false,
23775     imageRoot: '/',
23776     /**
23777      * @cfg {String/Object} Label for the strength meter (defaults to
23778      * 'Password strength:')
23779      */
23780     // private
23781     meterLabel: '',
23782     /**
23783      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23784      * ['Weak', 'Medium', 'Strong'])
23785      */
23786     // private    
23787     pwdStrengths: false,    
23788     // private
23789     strength: 0,
23790     // private
23791     _lastPwd: null,
23792     // private
23793     kCapitalLetter: 0,
23794     kSmallLetter: 1,
23795     kDigit: 2,
23796     kPunctuation: 3,
23797     
23798     insecure: false,
23799     // private
23800     initEvents: function ()
23801     {
23802         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23803
23804         if (this.el.is('input[type=password]') && Roo.isSafari) {
23805             this.el.on('keydown', this.SafariOnKeyDown, this);
23806         }
23807
23808         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23809     },
23810     // private
23811     onRender: function (ct, position)
23812     {
23813         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23814         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23815         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23816
23817         this.trigger.createChild({
23818                    cn: [
23819                     {
23820                     //id: 'PwdMeter',
23821                     tag: 'div',
23822                     cls: 'roo-password-meter-grey col-xs-12',
23823                     style: {
23824                         //width: 0,
23825                         //width: this.meterWidth + 'px'                                                
23826                         }
23827                     },
23828                     {                            
23829                          cls: 'roo-password-meter-text'                          
23830                     }
23831                 ]            
23832         });
23833
23834          
23835         if (this.hideTrigger) {
23836             this.trigger.setDisplayed(false);
23837         }
23838         this.setSize(this.width || '', this.height || '');
23839     },
23840     // private
23841     onDestroy: function ()
23842     {
23843         if (this.trigger) {
23844             this.trigger.removeAllListeners();
23845             this.trigger.remove();
23846         }
23847         if (this.wrap) {
23848             this.wrap.remove();
23849         }
23850         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23851     },
23852     // private
23853     checkStrength: function ()
23854     {
23855         var pwd = this.inputEl().getValue();
23856         if (pwd == this._lastPwd) {
23857             return;
23858         }
23859
23860         var strength;
23861         if (this.ClientSideStrongPassword(pwd)) {
23862             strength = 3;
23863         } else if (this.ClientSideMediumPassword(pwd)) {
23864             strength = 2;
23865         } else if (this.ClientSideWeakPassword(pwd)) {
23866             strength = 1;
23867         } else {
23868             strength = 0;
23869         }
23870         
23871         Roo.log('strength1: ' + strength);
23872         
23873         //var pm = this.trigger.child('div/div/div').dom;
23874         var pm = this.trigger.child('div/div');
23875         pm.removeClass(this.meterClass);
23876         pm.addClass(this.meterClass[strength]);
23877                 
23878         
23879         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23880                 
23881         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23882         
23883         this._lastPwd = pwd;
23884     },
23885     reset: function ()
23886     {
23887         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23888         
23889         this._lastPwd = '';
23890         
23891         var pm = this.trigger.child('div/div');
23892         pm.removeClass(this.meterClass);
23893         pm.addClass('roo-password-meter-grey');        
23894         
23895         
23896         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23897         
23898         pt.innerHTML = '';
23899         this.inputEl().dom.type='password';
23900     },
23901     // private
23902     validateValue: function (value)
23903     {
23904         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23905             return false;
23906         }
23907         if (value.length == 0) {
23908             if (this.allowBlank) {
23909                 this.clearInvalid();
23910                 return true;
23911             }
23912
23913             this.markInvalid(this.errors.PwdEmpty);
23914             this.errorMsg = this.errors.PwdEmpty;
23915             return false;
23916         }
23917         
23918         if(this.insecure){
23919             return true;
23920         }
23921         
23922         if (!value.match(/[\x21-\x7e]+/)) {
23923             this.markInvalid(this.errors.PwdBadChar);
23924             this.errorMsg = this.errors.PwdBadChar;
23925             return false;
23926         }
23927         if (value.length < 6) {
23928             this.markInvalid(this.errors.PwdShort);
23929             this.errorMsg = this.errors.PwdShort;
23930             return false;
23931         }
23932         if (value.length > 16) {
23933             this.markInvalid(this.errors.PwdLong);
23934             this.errorMsg = this.errors.PwdLong;
23935             return false;
23936         }
23937         var strength;
23938         if (this.ClientSideStrongPassword(value)) {
23939             strength = 3;
23940         } else if (this.ClientSideMediumPassword(value)) {
23941             strength = 2;
23942         } else if (this.ClientSideWeakPassword(value)) {
23943             strength = 1;
23944         } else {
23945             strength = 0;
23946         }
23947
23948         
23949         if (strength < 2) {
23950             //this.markInvalid(this.errors.TooWeak);
23951             this.errorMsg = this.errors.TooWeak;
23952             //return false;
23953         }
23954         
23955         
23956         console.log('strength2: ' + strength);
23957         
23958         //var pm = this.trigger.child('div/div/div').dom;
23959         
23960         var pm = this.trigger.child('div/div');
23961         pm.removeClass(this.meterClass);
23962         pm.addClass(this.meterClass[strength]);
23963                 
23964         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23965                 
23966         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23967         
23968         this.errorMsg = ''; 
23969         return true;
23970     },
23971     // private
23972     CharacterSetChecks: function (type)
23973     {
23974         this.type = type;
23975         this.fResult = false;
23976     },
23977     // private
23978     isctype: function (character, type)
23979     {
23980         switch (type) {  
23981             case this.kCapitalLetter:
23982                 if (character >= 'A' && character <= 'Z') {
23983                     return true;
23984                 }
23985                 break;
23986             
23987             case this.kSmallLetter:
23988                 if (character >= 'a' && character <= 'z') {
23989                     return true;
23990                 }
23991                 break;
23992             
23993             case this.kDigit:
23994                 if (character >= '0' && character <= '9') {
23995                     return true;
23996                 }
23997                 break;
23998             
23999             case this.kPunctuation:
24000                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
24001                     return true;
24002                 }
24003                 break;
24004             
24005             default:
24006                 return false;
24007         }
24008
24009     },
24010     // private
24011     IsLongEnough: function (pwd, size)
24012     {
24013         return !(pwd == null || isNaN(size) || pwd.length < size);
24014     },
24015     // private
24016     SpansEnoughCharacterSets: function (word, nb)
24017     {
24018         if (!this.IsLongEnough(word, nb))
24019         {
24020             return false;
24021         }
24022
24023         var characterSetChecks = new Array(
24024             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
24025             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
24026         );
24027         
24028         for (var index = 0; index < word.length; ++index) {
24029             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24030                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
24031                     characterSetChecks[nCharSet].fResult = true;
24032                     break;
24033                 }
24034             }
24035         }
24036
24037         var nCharSets = 0;
24038         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
24039             if (characterSetChecks[nCharSet].fResult) {
24040                 ++nCharSets;
24041             }
24042         }
24043
24044         if (nCharSets < nb) {
24045             return false;
24046         }
24047         return true;
24048     },
24049     // private
24050     ClientSideStrongPassword: function (pwd)
24051     {
24052         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
24053     },
24054     // private
24055     ClientSideMediumPassword: function (pwd)
24056     {
24057         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
24058     },
24059     // private
24060     ClientSideWeakPassword: function (pwd)
24061     {
24062         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
24063     }
24064           
24065 })//<script type="text/javascript">
24066
24067 /*
24068  * Based  Ext JS Library 1.1.1
24069  * Copyright(c) 2006-2007, Ext JS, LLC.
24070  * LGPL
24071  *
24072  */
24073  
24074 /**
24075  * @class Roo.HtmlEditorCore
24076  * @extends Roo.Component
24077  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
24078  *
24079  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
24080  */
24081
24082 Roo.HtmlEditorCore = function(config){
24083     
24084     
24085     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
24086     
24087     
24088     this.addEvents({
24089         /**
24090          * @event initialize
24091          * Fires when the editor is fully initialized (including the iframe)
24092          * @param {Roo.HtmlEditorCore} this
24093          */
24094         initialize: true,
24095         /**
24096          * @event activate
24097          * Fires when the editor is first receives the focus. Any insertion must wait
24098          * until after this event.
24099          * @param {Roo.HtmlEditorCore} this
24100          */
24101         activate: true,
24102          /**
24103          * @event beforesync
24104          * Fires before the textarea is updated with content from the editor iframe. Return false
24105          * to cancel the sync.
24106          * @param {Roo.HtmlEditorCore} this
24107          * @param {String} html
24108          */
24109         beforesync: true,
24110          /**
24111          * @event beforepush
24112          * Fires before the iframe editor is updated with content from the textarea. Return false
24113          * to cancel the push.
24114          * @param {Roo.HtmlEditorCore} this
24115          * @param {String} html
24116          */
24117         beforepush: true,
24118          /**
24119          * @event sync
24120          * Fires when the textarea is updated with content from the editor iframe.
24121          * @param {Roo.HtmlEditorCore} this
24122          * @param {String} html
24123          */
24124         sync: true,
24125          /**
24126          * @event push
24127          * Fires when the iframe editor is updated with content from the textarea.
24128          * @param {Roo.HtmlEditorCore} this
24129          * @param {String} html
24130          */
24131         push: true,
24132         
24133         /**
24134          * @event editorevent
24135          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24136          * @param {Roo.HtmlEditorCore} this
24137          */
24138         editorevent: true
24139         
24140     });
24141     
24142     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24143     
24144     // defaults : white / black...
24145     this.applyBlacklists();
24146     
24147     
24148     
24149 };
24150
24151
24152 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24153
24154
24155      /**
24156      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24157      */
24158     
24159     owner : false,
24160     
24161      /**
24162      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24163      *                        Roo.resizable.
24164      */
24165     resizable : false,
24166      /**
24167      * @cfg {Number} height (in pixels)
24168      */   
24169     height: 300,
24170    /**
24171      * @cfg {Number} width (in pixels)
24172      */   
24173     width: 500,
24174     
24175     /**
24176      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24177      * 
24178      */
24179     stylesheets: false,
24180     
24181     // id of frame..
24182     frameId: false,
24183     
24184     // private properties
24185     validationEvent : false,
24186     deferHeight: true,
24187     initialized : false,
24188     activated : false,
24189     sourceEditMode : false,
24190     onFocus : Roo.emptyFn,
24191     iframePad:3,
24192     hideMode:'offsets',
24193     
24194     clearUp: true,
24195     
24196     // blacklist + whitelisted elements..
24197     black: false,
24198     white: false,
24199      
24200     bodyCls : '',
24201
24202     /**
24203      * Protected method that will not generally be called directly. It
24204      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24205      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24206      */
24207     getDocMarkup : function(){
24208         // body styles..
24209         var st = '';
24210         
24211         // inherit styels from page...?? 
24212         if (this.stylesheets === false) {
24213             
24214             Roo.get(document.head).select('style').each(function(node) {
24215                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24216             });
24217             
24218             Roo.get(document.head).select('link').each(function(node) { 
24219                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24220             });
24221             
24222         } else if (!this.stylesheets.length) {
24223                 // simple..
24224                 st = '<style type="text/css">' +
24225                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24226                    '</style>';
24227         } else {
24228             for (var i in this.stylesheets) { 
24229                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24230             }
24231             
24232         }
24233         
24234         st +=  '<style type="text/css">' +
24235             'IMG { cursor: pointer } ' +
24236         '</style>';
24237
24238         var cls = 'roo-htmleditor-body';
24239         
24240         if(this.bodyCls.length){
24241             cls += ' ' + this.bodyCls;
24242         }
24243         
24244         return '<html><head>' + st  +
24245             //<style type="text/css">' +
24246             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24247             //'</style>' +
24248             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24249     },
24250
24251     // private
24252     onRender : function(ct, position)
24253     {
24254         var _t = this;
24255         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24256         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24257         
24258         
24259         this.el.dom.style.border = '0 none';
24260         this.el.dom.setAttribute('tabIndex', -1);
24261         this.el.addClass('x-hidden hide');
24262         
24263         
24264         
24265         if(Roo.isIE){ // fix IE 1px bogus margin
24266             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24267         }
24268        
24269         
24270         this.frameId = Roo.id();
24271         
24272          
24273         
24274         var iframe = this.owner.wrap.createChild({
24275             tag: 'iframe',
24276             cls: 'form-control', // bootstrap..
24277             id: this.frameId,
24278             name: this.frameId,
24279             frameBorder : 'no',
24280             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24281         }, this.el
24282         );
24283         
24284         
24285         this.iframe = iframe.dom;
24286
24287          this.assignDocWin();
24288         
24289         this.doc.designMode = 'on';
24290        
24291         this.doc.open();
24292         this.doc.write(this.getDocMarkup());
24293         this.doc.close();
24294
24295         
24296         var task = { // must defer to wait for browser to be ready
24297             run : function(){
24298                 //console.log("run task?" + this.doc.readyState);
24299                 this.assignDocWin();
24300                 if(this.doc.body || this.doc.readyState == 'complete'){
24301                     try {
24302                         this.doc.designMode="on";
24303                     } catch (e) {
24304                         return;
24305                     }
24306                     Roo.TaskMgr.stop(task);
24307                     this.initEditor.defer(10, this);
24308                 }
24309             },
24310             interval : 10,
24311             duration: 10000,
24312             scope: this
24313         };
24314         Roo.TaskMgr.start(task);
24315
24316     },
24317
24318     // private
24319     onResize : function(w, h)
24320     {
24321          Roo.log('resize: ' +w + ',' + h );
24322         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24323         if(!this.iframe){
24324             return;
24325         }
24326         if(typeof w == 'number'){
24327             
24328             this.iframe.style.width = w + 'px';
24329         }
24330         if(typeof h == 'number'){
24331             
24332             this.iframe.style.height = h + 'px';
24333             if(this.doc){
24334                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24335             }
24336         }
24337         
24338     },
24339
24340     /**
24341      * Toggles the editor between standard and source edit mode.
24342      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24343      */
24344     toggleSourceEdit : function(sourceEditMode){
24345         
24346         this.sourceEditMode = sourceEditMode === true;
24347         
24348         if(this.sourceEditMode){
24349  
24350             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24351             
24352         }else{
24353             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24354             //this.iframe.className = '';
24355             this.deferFocus();
24356         }
24357         //this.setSize(this.owner.wrap.getSize());
24358         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24359     },
24360
24361     
24362   
24363
24364     /**
24365      * Protected method that will not generally be called directly. If you need/want
24366      * custom HTML cleanup, this is the method you should override.
24367      * @param {String} html The HTML to be cleaned
24368      * return {String} The cleaned HTML
24369      */
24370     cleanHtml : function(html){
24371         html = String(html);
24372         if(html.length > 5){
24373             if(Roo.isSafari){ // strip safari nonsense
24374                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24375             }
24376         }
24377         if(html == '&nbsp;'){
24378             html = '';
24379         }
24380         return html;
24381     },
24382
24383     /**
24384      * HTML Editor -> Textarea
24385      * Protected method that will not generally be called directly. Syncs the contents
24386      * of the editor iframe with the textarea.
24387      */
24388     syncValue : function(){
24389         if(this.initialized){
24390             var bd = (this.doc.body || this.doc.documentElement);
24391             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24392             var html = bd.innerHTML;
24393             if(Roo.isSafari){
24394                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24395                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24396                 if(m && m[1]){
24397                     html = '<div style="'+m[0]+'">' + html + '</div>';
24398                 }
24399             }
24400             html = this.cleanHtml(html);
24401             // fix up the special chars.. normaly like back quotes in word...
24402             // however we do not want to do this with chinese..
24403             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24404                 
24405                 var cc = match.charCodeAt();
24406
24407                 // Get the character value, handling surrogate pairs
24408                 if (match.length == 2) {
24409                     // It's a surrogate pair, calculate the Unicode code point
24410                     var high = match.charCodeAt(0) - 0xD800;
24411                     var low  = match.charCodeAt(1) - 0xDC00;
24412                     cc = (high * 0x400) + low + 0x10000;
24413                 }  else if (
24414                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24415                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24416                     (cc >= 0xf900 && cc < 0xfb00 )
24417                 ) {
24418                         return match;
24419                 }  
24420          
24421                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24422                 return "&#" + cc + ";";
24423                 
24424                 
24425             });
24426             
24427             
24428              
24429             if(this.owner.fireEvent('beforesync', this, html) !== false){
24430                 this.el.dom.value = html;
24431                 this.owner.fireEvent('sync', this, html);
24432             }
24433         }
24434     },
24435
24436     /**
24437      * Protected method that will not generally be called directly. Pushes the value of the textarea
24438      * into the iframe editor.
24439      */
24440     pushValue : function(){
24441         if(this.initialized){
24442             var v = this.el.dom.value.trim();
24443             
24444 //            if(v.length < 1){
24445 //                v = '&#160;';
24446 //            }
24447             
24448             if(this.owner.fireEvent('beforepush', this, v) !== false){
24449                 var d = (this.doc.body || this.doc.documentElement);
24450                 d.innerHTML = v;
24451                 this.cleanUpPaste();
24452                 this.el.dom.value = d.innerHTML;
24453                 this.owner.fireEvent('push', this, v);
24454             }
24455         }
24456     },
24457
24458     // private
24459     deferFocus : function(){
24460         this.focus.defer(10, this);
24461     },
24462
24463     // doc'ed in Field
24464     focus : function(){
24465         if(this.win && !this.sourceEditMode){
24466             this.win.focus();
24467         }else{
24468             this.el.focus();
24469         }
24470     },
24471     
24472     assignDocWin: function()
24473     {
24474         var iframe = this.iframe;
24475         
24476          if(Roo.isIE){
24477             this.doc = iframe.contentWindow.document;
24478             this.win = iframe.contentWindow;
24479         } else {
24480 //            if (!Roo.get(this.frameId)) {
24481 //                return;
24482 //            }
24483 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24484 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24485             
24486             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24487                 return;
24488             }
24489             
24490             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24491             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24492         }
24493     },
24494     
24495     // private
24496     initEditor : function(){
24497         //console.log("INIT EDITOR");
24498         this.assignDocWin();
24499         
24500         
24501         
24502         this.doc.designMode="on";
24503         this.doc.open();
24504         this.doc.write(this.getDocMarkup());
24505         this.doc.close();
24506         
24507         var dbody = (this.doc.body || this.doc.documentElement);
24508         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24509         // this copies styles from the containing element into thsi one..
24510         // not sure why we need all of this..
24511         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24512         
24513         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24514         //ss['background-attachment'] = 'fixed'; // w3c
24515         dbody.bgProperties = 'fixed'; // ie
24516         //Roo.DomHelper.applyStyles(dbody, ss);
24517         Roo.EventManager.on(this.doc, {
24518             //'mousedown': this.onEditorEvent,
24519             'mouseup': this.onEditorEvent,
24520             'dblclick': this.onEditorEvent,
24521             'click': this.onEditorEvent,
24522             'keyup': this.onEditorEvent,
24523             buffer:100,
24524             scope: this
24525         });
24526         if(Roo.isGecko){
24527             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24528         }
24529         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24530             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24531         }
24532         this.initialized = true;
24533
24534         this.owner.fireEvent('initialize', this);
24535         this.pushValue();
24536     },
24537
24538     // private
24539     onDestroy : function(){
24540         
24541         
24542         
24543         if(this.rendered){
24544             
24545             //for (var i =0; i < this.toolbars.length;i++) {
24546             //    // fixme - ask toolbars for heights?
24547             //    this.toolbars[i].onDestroy();
24548            // }
24549             
24550             //this.wrap.dom.innerHTML = '';
24551             //this.wrap.remove();
24552         }
24553     },
24554
24555     // private
24556     onFirstFocus : function(){
24557         
24558         this.assignDocWin();
24559         
24560         
24561         this.activated = true;
24562          
24563     
24564         if(Roo.isGecko){ // prevent silly gecko errors
24565             this.win.focus();
24566             var s = this.win.getSelection();
24567             if(!s.focusNode || s.focusNode.nodeType != 3){
24568                 var r = s.getRangeAt(0);
24569                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24570                 r.collapse(true);
24571                 this.deferFocus();
24572             }
24573             try{
24574                 this.execCmd('useCSS', true);
24575                 this.execCmd('styleWithCSS', false);
24576             }catch(e){}
24577         }
24578         this.owner.fireEvent('activate', this);
24579     },
24580
24581     // private
24582     adjustFont: function(btn){
24583         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24584         //if(Roo.isSafari){ // safari
24585         //    adjust *= 2;
24586        // }
24587         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24588         if(Roo.isSafari){ // safari
24589             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24590             v =  (v < 10) ? 10 : v;
24591             v =  (v > 48) ? 48 : v;
24592             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24593             
24594         }
24595         
24596         
24597         v = Math.max(1, v+adjust);
24598         
24599         this.execCmd('FontSize', v  );
24600     },
24601
24602     onEditorEvent : function(e)
24603     {
24604         this.owner.fireEvent('editorevent', this, e);
24605       //  this.updateToolbar();
24606         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24607     },
24608
24609     insertTag : function(tg)
24610     {
24611         // could be a bit smarter... -> wrap the current selected tRoo..
24612         if (tg.toLowerCase() == 'span' ||
24613             tg.toLowerCase() == 'code' ||
24614             tg.toLowerCase() == 'sup' ||
24615             tg.toLowerCase() == 'sub' 
24616             ) {
24617             
24618             range = this.createRange(this.getSelection());
24619             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24620             wrappingNode.appendChild(range.extractContents());
24621             range.insertNode(wrappingNode);
24622
24623             return;
24624             
24625             
24626             
24627         }
24628         this.execCmd("formatblock",   tg);
24629         
24630     },
24631     
24632     insertText : function(txt)
24633     {
24634         
24635         
24636         var range = this.createRange();
24637         range.deleteContents();
24638                //alert(Sender.getAttribute('label'));
24639                
24640         range.insertNode(this.doc.createTextNode(txt));
24641     } ,
24642     
24643      
24644
24645     /**
24646      * Executes a Midas editor command on the editor document and performs necessary focus and
24647      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24648      * @param {String} cmd The Midas command
24649      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24650      */
24651     relayCmd : function(cmd, value){
24652         this.win.focus();
24653         this.execCmd(cmd, value);
24654         this.owner.fireEvent('editorevent', this);
24655         //this.updateToolbar();
24656         this.owner.deferFocus();
24657     },
24658
24659     /**
24660      * Executes a Midas editor command directly on the editor document.
24661      * For visual commands, you should use {@link #relayCmd} instead.
24662      * <b>This should only be called after the editor is initialized.</b>
24663      * @param {String} cmd The Midas command
24664      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24665      */
24666     execCmd : function(cmd, value){
24667         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24668         this.syncValue();
24669     },
24670  
24671  
24672    
24673     /**
24674      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24675      * to insert tRoo.
24676      * @param {String} text | dom node.. 
24677      */
24678     insertAtCursor : function(text)
24679     {
24680         
24681         if(!this.activated){
24682             return;
24683         }
24684         /*
24685         if(Roo.isIE){
24686             this.win.focus();
24687             var r = this.doc.selection.createRange();
24688             if(r){
24689                 r.collapse(true);
24690                 r.pasteHTML(text);
24691                 this.syncValue();
24692                 this.deferFocus();
24693             
24694             }
24695             return;
24696         }
24697         */
24698         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24699             this.win.focus();
24700             
24701             
24702             // from jquery ui (MIT licenced)
24703             var range, node;
24704             var win = this.win;
24705             
24706             if (win.getSelection && win.getSelection().getRangeAt) {
24707                 range = win.getSelection().getRangeAt(0);
24708                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24709                 range.insertNode(node);
24710             } else if (win.document.selection && win.document.selection.createRange) {
24711                 // no firefox support
24712                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24713                 win.document.selection.createRange().pasteHTML(txt);
24714             } else {
24715                 // no firefox support
24716                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24717                 this.execCmd('InsertHTML', txt);
24718             } 
24719             
24720             this.syncValue();
24721             
24722             this.deferFocus();
24723         }
24724     },
24725  // private
24726     mozKeyPress : function(e){
24727         if(e.ctrlKey){
24728             var c = e.getCharCode(), cmd;
24729           
24730             if(c > 0){
24731                 c = String.fromCharCode(c).toLowerCase();
24732                 switch(c){
24733                     case 'b':
24734                         cmd = 'bold';
24735                         break;
24736                     case 'i':
24737                         cmd = 'italic';
24738                         break;
24739                     
24740                     case 'u':
24741                         cmd = 'underline';
24742                         break;
24743                     
24744                     case 'v':
24745                         this.cleanUpPaste.defer(100, this);
24746                         return;
24747                         
24748                 }
24749                 if(cmd){
24750                     this.win.focus();
24751                     this.execCmd(cmd);
24752                     this.deferFocus();
24753                     e.preventDefault();
24754                 }
24755                 
24756             }
24757         }
24758     },
24759
24760     // private
24761     fixKeys : function(){ // load time branching for fastest keydown performance
24762         if(Roo.isIE){
24763             return function(e){
24764                 var k = e.getKey(), r;
24765                 if(k == e.TAB){
24766                     e.stopEvent();
24767                     r = this.doc.selection.createRange();
24768                     if(r){
24769                         r.collapse(true);
24770                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24771                         this.deferFocus();
24772                     }
24773                     return;
24774                 }
24775                 
24776                 if(k == e.ENTER){
24777                     r = this.doc.selection.createRange();
24778                     if(r){
24779                         var target = r.parentElement();
24780                         if(!target || target.tagName.toLowerCase() != 'li'){
24781                             e.stopEvent();
24782                             r.pasteHTML('<br />');
24783                             r.collapse(false);
24784                             r.select();
24785                         }
24786                     }
24787                 }
24788                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24789                     this.cleanUpPaste.defer(100, this);
24790                     return;
24791                 }
24792                 
24793                 
24794             };
24795         }else if(Roo.isOpera){
24796             return function(e){
24797                 var k = e.getKey();
24798                 if(k == e.TAB){
24799                     e.stopEvent();
24800                     this.win.focus();
24801                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24802                     this.deferFocus();
24803                 }
24804                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24805                     this.cleanUpPaste.defer(100, this);
24806                     return;
24807                 }
24808                 
24809             };
24810         }else if(Roo.isSafari){
24811             return function(e){
24812                 var k = e.getKey();
24813                 
24814                 if(k == e.TAB){
24815                     e.stopEvent();
24816                     this.execCmd('InsertText','\t');
24817                     this.deferFocus();
24818                     return;
24819                 }
24820                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24821                     this.cleanUpPaste.defer(100, this);
24822                     return;
24823                 }
24824                 
24825              };
24826         }
24827     }(),
24828     
24829     getAllAncestors: function()
24830     {
24831         var p = this.getSelectedNode();
24832         var a = [];
24833         if (!p) {
24834             a.push(p); // push blank onto stack..
24835             p = this.getParentElement();
24836         }
24837         
24838         
24839         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24840             a.push(p);
24841             p = p.parentNode;
24842         }
24843         a.push(this.doc.body);
24844         return a;
24845     },
24846     lastSel : false,
24847     lastSelNode : false,
24848     
24849     
24850     getSelection : function() 
24851     {
24852         this.assignDocWin();
24853         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24854     },
24855     
24856     getSelectedNode: function() 
24857     {
24858         // this may only work on Gecko!!!
24859         
24860         // should we cache this!!!!
24861         
24862         
24863         
24864          
24865         var range = this.createRange(this.getSelection()).cloneRange();
24866         
24867         if (Roo.isIE) {
24868             var parent = range.parentElement();
24869             while (true) {
24870                 var testRange = range.duplicate();
24871                 testRange.moveToElementText(parent);
24872                 if (testRange.inRange(range)) {
24873                     break;
24874                 }
24875                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24876                     break;
24877                 }
24878                 parent = parent.parentElement;
24879             }
24880             return parent;
24881         }
24882         
24883         // is ancestor a text element.
24884         var ac =  range.commonAncestorContainer;
24885         if (ac.nodeType == 3) {
24886             ac = ac.parentNode;
24887         }
24888         
24889         var ar = ac.childNodes;
24890          
24891         var nodes = [];
24892         var other_nodes = [];
24893         var has_other_nodes = false;
24894         for (var i=0;i<ar.length;i++) {
24895             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24896                 continue;
24897             }
24898             // fullly contained node.
24899             
24900             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24901                 nodes.push(ar[i]);
24902                 continue;
24903             }
24904             
24905             // probably selected..
24906             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24907                 other_nodes.push(ar[i]);
24908                 continue;
24909             }
24910             // outer..
24911             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24912                 continue;
24913             }
24914             
24915             
24916             has_other_nodes = true;
24917         }
24918         if (!nodes.length && other_nodes.length) {
24919             nodes= other_nodes;
24920         }
24921         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24922             return false;
24923         }
24924         
24925         return nodes[0];
24926     },
24927     createRange: function(sel)
24928     {
24929         // this has strange effects when using with 
24930         // top toolbar - not sure if it's a great idea.
24931         //this.editor.contentWindow.focus();
24932         if (typeof sel != "undefined") {
24933             try {
24934                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24935             } catch(e) {
24936                 return this.doc.createRange();
24937             }
24938         } else {
24939             return this.doc.createRange();
24940         }
24941     },
24942     getParentElement: function()
24943     {
24944         
24945         this.assignDocWin();
24946         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24947         
24948         var range = this.createRange(sel);
24949          
24950         try {
24951             var p = range.commonAncestorContainer;
24952             while (p.nodeType == 3) { // text node
24953                 p = p.parentNode;
24954             }
24955             return p;
24956         } catch (e) {
24957             return null;
24958         }
24959     
24960     },
24961     /***
24962      *
24963      * Range intersection.. the hard stuff...
24964      *  '-1' = before
24965      *  '0' = hits..
24966      *  '1' = after.
24967      *         [ -- selected range --- ]
24968      *   [fail]                        [fail]
24969      *
24970      *    basically..
24971      *      if end is before start or  hits it. fail.
24972      *      if start is after end or hits it fail.
24973      *
24974      *   if either hits (but other is outside. - then it's not 
24975      *   
24976      *    
24977      **/
24978     
24979     
24980     // @see http://www.thismuchiknow.co.uk/?p=64.
24981     rangeIntersectsNode : function(range, node)
24982     {
24983         var nodeRange = node.ownerDocument.createRange();
24984         try {
24985             nodeRange.selectNode(node);
24986         } catch (e) {
24987             nodeRange.selectNodeContents(node);
24988         }
24989     
24990         var rangeStartRange = range.cloneRange();
24991         rangeStartRange.collapse(true);
24992     
24993         var rangeEndRange = range.cloneRange();
24994         rangeEndRange.collapse(false);
24995     
24996         var nodeStartRange = nodeRange.cloneRange();
24997         nodeStartRange.collapse(true);
24998     
24999         var nodeEndRange = nodeRange.cloneRange();
25000         nodeEndRange.collapse(false);
25001     
25002         return rangeStartRange.compareBoundaryPoints(
25003                  Range.START_TO_START, nodeEndRange) == -1 &&
25004                rangeEndRange.compareBoundaryPoints(
25005                  Range.START_TO_START, nodeStartRange) == 1;
25006         
25007          
25008     },
25009     rangeCompareNode : function(range, node)
25010     {
25011         var nodeRange = node.ownerDocument.createRange();
25012         try {
25013             nodeRange.selectNode(node);
25014         } catch (e) {
25015             nodeRange.selectNodeContents(node);
25016         }
25017         
25018         
25019         range.collapse(true);
25020     
25021         nodeRange.collapse(true);
25022      
25023         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
25024         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
25025          
25026         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
25027         
25028         var nodeIsBefore   =  ss == 1;
25029         var nodeIsAfter    = ee == -1;
25030         
25031         if (nodeIsBefore && nodeIsAfter) {
25032             return 0; // outer
25033         }
25034         if (!nodeIsBefore && nodeIsAfter) {
25035             return 1; //right trailed.
25036         }
25037         
25038         if (nodeIsBefore && !nodeIsAfter) {
25039             return 2;  // left trailed.
25040         }
25041         // fully contined.
25042         return 3;
25043     },
25044
25045     // private? - in a new class?
25046     cleanUpPaste :  function()
25047     {
25048         // cleans up the whole document..
25049         Roo.log('cleanuppaste');
25050         
25051         this.cleanUpChildren(this.doc.body);
25052         var clean = this.cleanWordChars(this.doc.body.innerHTML);
25053         if (clean != this.doc.body.innerHTML) {
25054             this.doc.body.innerHTML = clean;
25055         }
25056         
25057     },
25058     
25059     cleanWordChars : function(input) {// change the chars to hex code
25060         var he = Roo.HtmlEditorCore;
25061         
25062         var output = input;
25063         Roo.each(he.swapCodes, function(sw) { 
25064             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
25065             
25066             output = output.replace(swapper, sw[1]);
25067         });
25068         
25069         return output;
25070     },
25071     
25072     
25073     cleanUpChildren : function (n)
25074     {
25075         if (!n.childNodes.length) {
25076             return;
25077         }
25078         for (var i = n.childNodes.length-1; i > -1 ; i--) {
25079            this.cleanUpChild(n.childNodes[i]);
25080         }
25081     },
25082     
25083     
25084         
25085     
25086     cleanUpChild : function (node)
25087     {
25088         var ed = this;
25089         //console.log(node);
25090         if (node.nodeName == "#text") {
25091             // clean up silly Windows -- stuff?
25092             return; 
25093         }
25094         if (node.nodeName == "#comment") {
25095             node.parentNode.removeChild(node);
25096             // clean up silly Windows -- stuff?
25097             return; 
25098         }
25099         var lcname = node.tagName.toLowerCase();
25100         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
25101         // whitelist of tags..
25102         
25103         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
25104             // remove node.
25105             node.parentNode.removeChild(node);
25106             return;
25107             
25108         }
25109         
25110         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
25111         
25112         // spans with no attributes - just remove them..
25113         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
25114             remove_keep_children = true;
25115         }
25116         
25117         // remove <a name=....> as rendering on yahoo mailer is borked with this.
25118         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
25119         
25120         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
25121         //    remove_keep_children = true;
25122         //}
25123         
25124         if (remove_keep_children) {
25125             this.cleanUpChildren(node);
25126             // inserts everything just before this node...
25127             while (node.childNodes.length) {
25128                 var cn = node.childNodes[0];
25129                 node.removeChild(cn);
25130                 node.parentNode.insertBefore(cn, node);
25131             }
25132             node.parentNode.removeChild(node);
25133             return;
25134         }
25135         
25136         if (!node.attributes || !node.attributes.length) {
25137             
25138           
25139             
25140             
25141             this.cleanUpChildren(node);
25142             return;
25143         }
25144         
25145         function cleanAttr(n,v)
25146         {
25147             
25148             if (v.match(/^\./) || v.match(/^\//)) {
25149                 return;
25150             }
25151             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25152                 return;
25153             }
25154             if (v.match(/^#/)) {
25155                 return;
25156             }
25157             if (v.match(/^\{/)) { // allow template editing.
25158                 return;
25159             }
25160 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25161             node.removeAttribute(n);
25162             
25163         }
25164         
25165         var cwhite = this.cwhite;
25166         var cblack = this.cblack;
25167             
25168         function cleanStyle(n,v)
25169         {
25170             if (v.match(/expression/)) { //XSS?? should we even bother..
25171                 node.removeAttribute(n);
25172                 return;
25173             }
25174             
25175             var parts = v.split(/;/);
25176             var clean = [];
25177             
25178             Roo.each(parts, function(p) {
25179                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25180                 if (!p.length) {
25181                     return true;
25182                 }
25183                 var l = p.split(':').shift().replace(/\s+/g,'');
25184                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25185                 
25186                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25187 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25188                     //node.removeAttribute(n);
25189                     return true;
25190                 }
25191                 //Roo.log()
25192                 // only allow 'c whitelisted system attributes'
25193                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25194 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25195                     //node.removeAttribute(n);
25196                     return true;
25197                 }
25198                 
25199                 
25200                  
25201                 
25202                 clean.push(p);
25203                 return true;
25204             });
25205             if (clean.length) { 
25206                 node.setAttribute(n, clean.join(';'));
25207             } else {
25208                 node.removeAttribute(n);
25209             }
25210             
25211         }
25212         
25213         
25214         for (var i = node.attributes.length-1; i > -1 ; i--) {
25215             var a = node.attributes[i];
25216             //console.log(a);
25217             
25218             if (a.name.toLowerCase().substr(0,2)=='on')  {
25219                 node.removeAttribute(a.name);
25220                 continue;
25221             }
25222             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25223                 node.removeAttribute(a.name);
25224                 continue;
25225             }
25226             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25227                 cleanAttr(a.name,a.value); // fixme..
25228                 continue;
25229             }
25230             if (a.name == 'style') {
25231                 cleanStyle(a.name,a.value);
25232                 continue;
25233             }
25234             /// clean up MS crap..
25235             // tecnically this should be a list of valid class'es..
25236             
25237             
25238             if (a.name == 'class') {
25239                 if (a.value.match(/^Mso/)) {
25240                     node.removeAttribute('class');
25241                 }
25242                 
25243                 if (a.value.match(/^body$/)) {
25244                     node.removeAttribute('class');
25245                 }
25246                 continue;
25247             }
25248             
25249             // style cleanup!?
25250             // class cleanup?
25251             
25252         }
25253         
25254         
25255         this.cleanUpChildren(node);
25256         
25257         
25258     },
25259     
25260     /**
25261      * Clean up MS wordisms...
25262      */
25263     cleanWord : function(node)
25264     {
25265         if (!node) {
25266             this.cleanWord(this.doc.body);
25267             return;
25268         }
25269         
25270         if(
25271                 node.nodeName == 'SPAN' &&
25272                 !node.hasAttributes() &&
25273                 node.childNodes.length == 1 &&
25274                 node.firstChild.nodeName == "#text"  
25275         ) {
25276             var textNode = node.firstChild;
25277             node.removeChild(textNode);
25278             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25279                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25280             }
25281             node.parentNode.insertBefore(textNode, node);
25282             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25283                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25284             }
25285             node.parentNode.removeChild(node);
25286         }
25287         
25288         if (node.nodeName == "#text") {
25289             // clean up silly Windows -- stuff?
25290             return; 
25291         }
25292         if (node.nodeName == "#comment") {
25293             node.parentNode.removeChild(node);
25294             // clean up silly Windows -- stuff?
25295             return; 
25296         }
25297         
25298         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25299             node.parentNode.removeChild(node);
25300             return;
25301         }
25302         //Roo.log(node.tagName);
25303         // remove - but keep children..
25304         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25305             //Roo.log('-- removed');
25306             while (node.childNodes.length) {
25307                 var cn = node.childNodes[0];
25308                 node.removeChild(cn);
25309                 node.parentNode.insertBefore(cn, node);
25310                 // move node to parent - and clean it..
25311                 this.cleanWord(cn);
25312             }
25313             node.parentNode.removeChild(node);
25314             /// no need to iterate chidlren = it's got none..
25315             //this.iterateChildren(node, this.cleanWord);
25316             return;
25317         }
25318         // clean styles
25319         if (node.className.length) {
25320             
25321             var cn = node.className.split(/\W+/);
25322             var cna = [];
25323             Roo.each(cn, function(cls) {
25324                 if (cls.match(/Mso[a-zA-Z]+/)) {
25325                     return;
25326                 }
25327                 cna.push(cls);
25328             });
25329             node.className = cna.length ? cna.join(' ') : '';
25330             if (!cna.length) {
25331                 node.removeAttribute("class");
25332             }
25333         }
25334         
25335         if (node.hasAttribute("lang")) {
25336             node.removeAttribute("lang");
25337         }
25338         
25339         if (node.hasAttribute("style")) {
25340             
25341             var styles = node.getAttribute("style").split(";");
25342             var nstyle = [];
25343             Roo.each(styles, function(s) {
25344                 if (!s.match(/:/)) {
25345                     return;
25346                 }
25347                 var kv = s.split(":");
25348                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25349                     return;
25350                 }
25351                 // what ever is left... we allow.
25352                 nstyle.push(s);
25353             });
25354             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25355             if (!nstyle.length) {
25356                 node.removeAttribute('style');
25357             }
25358         }
25359         this.iterateChildren(node, this.cleanWord);
25360         
25361         
25362         
25363     },
25364     /**
25365      * iterateChildren of a Node, calling fn each time, using this as the scole..
25366      * @param {DomNode} node node to iterate children of.
25367      * @param {Function} fn method of this class to call on each item.
25368      */
25369     iterateChildren : function(node, fn)
25370     {
25371         if (!node.childNodes.length) {
25372                 return;
25373         }
25374         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25375            fn.call(this, node.childNodes[i])
25376         }
25377     },
25378     
25379     
25380     /**
25381      * cleanTableWidths.
25382      *
25383      * Quite often pasting from word etc.. results in tables with column and widths.
25384      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25385      *
25386      */
25387     cleanTableWidths : function(node)
25388     {
25389          
25390          
25391         if (!node) {
25392             this.cleanTableWidths(this.doc.body);
25393             return;
25394         }
25395         
25396         // ignore list...
25397         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25398             return; 
25399         }
25400         Roo.log(node.tagName);
25401         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25402             this.iterateChildren(node, this.cleanTableWidths);
25403             return;
25404         }
25405         if (node.hasAttribute('width')) {
25406             node.removeAttribute('width');
25407         }
25408         
25409          
25410         if (node.hasAttribute("style")) {
25411             // pretty basic...
25412             
25413             var styles = node.getAttribute("style").split(";");
25414             var nstyle = [];
25415             Roo.each(styles, function(s) {
25416                 if (!s.match(/:/)) {
25417                     return;
25418                 }
25419                 var kv = s.split(":");
25420                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25421                     return;
25422                 }
25423                 // what ever is left... we allow.
25424                 nstyle.push(s);
25425             });
25426             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25427             if (!nstyle.length) {
25428                 node.removeAttribute('style');
25429             }
25430         }
25431         
25432         this.iterateChildren(node, this.cleanTableWidths);
25433         
25434         
25435     },
25436     
25437     
25438     
25439     
25440     domToHTML : function(currentElement, depth, nopadtext) {
25441         
25442         depth = depth || 0;
25443         nopadtext = nopadtext || false;
25444     
25445         if (!currentElement) {
25446             return this.domToHTML(this.doc.body);
25447         }
25448         
25449         //Roo.log(currentElement);
25450         var j;
25451         var allText = false;
25452         var nodeName = currentElement.nodeName;
25453         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25454         
25455         if  (nodeName == '#text') {
25456             
25457             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25458         }
25459         
25460         
25461         var ret = '';
25462         if (nodeName != 'BODY') {
25463              
25464             var i = 0;
25465             // Prints the node tagName, such as <A>, <IMG>, etc
25466             if (tagName) {
25467                 var attr = [];
25468                 for(i = 0; i < currentElement.attributes.length;i++) {
25469                     // quoting?
25470                     var aname = currentElement.attributes.item(i).name;
25471                     if (!currentElement.attributes.item(i).value.length) {
25472                         continue;
25473                     }
25474                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25475                 }
25476                 
25477                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25478             } 
25479             else {
25480                 
25481                 // eack
25482             }
25483         } else {
25484             tagName = false;
25485         }
25486         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25487             return ret;
25488         }
25489         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25490             nopadtext = true;
25491         }
25492         
25493         
25494         // Traverse the tree
25495         i = 0;
25496         var currentElementChild = currentElement.childNodes.item(i);
25497         var allText = true;
25498         var innerHTML  = '';
25499         lastnode = '';
25500         while (currentElementChild) {
25501             // Formatting code (indent the tree so it looks nice on the screen)
25502             var nopad = nopadtext;
25503             if (lastnode == 'SPAN') {
25504                 nopad  = true;
25505             }
25506             // text
25507             if  (currentElementChild.nodeName == '#text') {
25508                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25509                 toadd = nopadtext ? toadd : toadd.trim();
25510                 if (!nopad && toadd.length > 80) {
25511                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25512                 }
25513                 innerHTML  += toadd;
25514                 
25515                 i++;
25516                 currentElementChild = currentElement.childNodes.item(i);
25517                 lastNode = '';
25518                 continue;
25519             }
25520             allText = false;
25521             
25522             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25523                 
25524             // Recursively traverse the tree structure of the child node
25525             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25526             lastnode = currentElementChild.nodeName;
25527             i++;
25528             currentElementChild=currentElement.childNodes.item(i);
25529         }
25530         
25531         ret += innerHTML;
25532         
25533         if (!allText) {
25534                 // The remaining code is mostly for formatting the tree
25535             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25536         }
25537         
25538         
25539         if (tagName) {
25540             ret+= "</"+tagName+">";
25541         }
25542         return ret;
25543         
25544     },
25545         
25546     applyBlacklists : function()
25547     {
25548         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25549         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25550         
25551         this.white = [];
25552         this.black = [];
25553         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25554             if (b.indexOf(tag) > -1) {
25555                 return;
25556             }
25557             this.white.push(tag);
25558             
25559         }, this);
25560         
25561         Roo.each(w, function(tag) {
25562             if (b.indexOf(tag) > -1) {
25563                 return;
25564             }
25565             if (this.white.indexOf(tag) > -1) {
25566                 return;
25567             }
25568             this.white.push(tag);
25569             
25570         }, this);
25571         
25572         
25573         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25574             if (w.indexOf(tag) > -1) {
25575                 return;
25576             }
25577             this.black.push(tag);
25578             
25579         }, this);
25580         
25581         Roo.each(b, function(tag) {
25582             if (w.indexOf(tag) > -1) {
25583                 return;
25584             }
25585             if (this.black.indexOf(tag) > -1) {
25586                 return;
25587             }
25588             this.black.push(tag);
25589             
25590         }, this);
25591         
25592         
25593         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25594         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25595         
25596         this.cwhite = [];
25597         this.cblack = [];
25598         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25599             if (b.indexOf(tag) > -1) {
25600                 return;
25601             }
25602             this.cwhite.push(tag);
25603             
25604         }, this);
25605         
25606         Roo.each(w, function(tag) {
25607             if (b.indexOf(tag) > -1) {
25608                 return;
25609             }
25610             if (this.cwhite.indexOf(tag) > -1) {
25611                 return;
25612             }
25613             this.cwhite.push(tag);
25614             
25615         }, this);
25616         
25617         
25618         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25619             if (w.indexOf(tag) > -1) {
25620                 return;
25621             }
25622             this.cblack.push(tag);
25623             
25624         }, this);
25625         
25626         Roo.each(b, function(tag) {
25627             if (w.indexOf(tag) > -1) {
25628                 return;
25629             }
25630             if (this.cblack.indexOf(tag) > -1) {
25631                 return;
25632             }
25633             this.cblack.push(tag);
25634             
25635         }, this);
25636     },
25637     
25638     setStylesheets : function(stylesheets)
25639     {
25640         if(typeof(stylesheets) == 'string'){
25641             Roo.get(this.iframe.contentDocument.head).createChild({
25642                 tag : 'link',
25643                 rel : 'stylesheet',
25644                 type : 'text/css',
25645                 href : stylesheets
25646             });
25647             
25648             return;
25649         }
25650         var _this = this;
25651      
25652         Roo.each(stylesheets, function(s) {
25653             if(!s.length){
25654                 return;
25655             }
25656             
25657             Roo.get(_this.iframe.contentDocument.head).createChild({
25658                 tag : 'link',
25659                 rel : 'stylesheet',
25660                 type : 'text/css',
25661                 href : s
25662             });
25663         });
25664
25665         
25666     },
25667     
25668     removeStylesheets : function()
25669     {
25670         var _this = this;
25671         
25672         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25673             s.remove();
25674         });
25675     },
25676     
25677     setStyle : function(style)
25678     {
25679         Roo.get(this.iframe.contentDocument.head).createChild({
25680             tag : 'style',
25681             type : 'text/css',
25682             html : style
25683         });
25684
25685         return;
25686     }
25687     
25688     // hide stuff that is not compatible
25689     /**
25690      * @event blur
25691      * @hide
25692      */
25693     /**
25694      * @event change
25695      * @hide
25696      */
25697     /**
25698      * @event focus
25699      * @hide
25700      */
25701     /**
25702      * @event specialkey
25703      * @hide
25704      */
25705     /**
25706      * @cfg {String} fieldClass @hide
25707      */
25708     /**
25709      * @cfg {String} focusClass @hide
25710      */
25711     /**
25712      * @cfg {String} autoCreate @hide
25713      */
25714     /**
25715      * @cfg {String} inputType @hide
25716      */
25717     /**
25718      * @cfg {String} invalidClass @hide
25719      */
25720     /**
25721      * @cfg {String} invalidText @hide
25722      */
25723     /**
25724      * @cfg {String} msgFx @hide
25725      */
25726     /**
25727      * @cfg {String} validateOnBlur @hide
25728      */
25729 });
25730
25731 Roo.HtmlEditorCore.white = [
25732         'area', 'br', 'img', 'input', 'hr', 'wbr',
25733         
25734        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25735        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25736        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25737        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25738        'table',   'ul',         'xmp', 
25739        
25740        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25741       'thead',   'tr', 
25742      
25743       'dir', 'menu', 'ol', 'ul', 'dl',
25744        
25745       'embed',  'object'
25746 ];
25747
25748
25749 Roo.HtmlEditorCore.black = [
25750     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25751         'applet', // 
25752         'base',   'basefont', 'bgsound', 'blink',  'body', 
25753         'frame',  'frameset', 'head',    'html',   'ilayer', 
25754         'iframe', 'layer',  'link',     'meta',    'object',   
25755         'script', 'style' ,'title',  'xml' // clean later..
25756 ];
25757 Roo.HtmlEditorCore.clean = [
25758     'script', 'style', 'title', 'xml'
25759 ];
25760 Roo.HtmlEditorCore.remove = [
25761     'font'
25762 ];
25763 // attributes..
25764
25765 Roo.HtmlEditorCore.ablack = [
25766     'on'
25767 ];
25768     
25769 Roo.HtmlEditorCore.aclean = [ 
25770     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25771 ];
25772
25773 // protocols..
25774 Roo.HtmlEditorCore.pwhite= [
25775         'http',  'https',  'mailto'
25776 ];
25777
25778 // white listed style attributes.
25779 Roo.HtmlEditorCore.cwhite= [
25780       //  'text-align', /// default is to allow most things..
25781       
25782          
25783 //        'font-size'//??
25784 ];
25785
25786 // black listed style attributes.
25787 Roo.HtmlEditorCore.cblack= [
25788       //  'font-size' -- this can be set by the project 
25789 ];
25790
25791
25792 Roo.HtmlEditorCore.swapCodes   =[ 
25793     [    8211, "--" ], 
25794     [    8212, "--" ], 
25795     [    8216,  "'" ],  
25796     [    8217, "'" ],  
25797     [    8220, '"' ],  
25798     [    8221, '"' ],  
25799     [    8226, "*" ],  
25800     [    8230, "..." ]
25801 ]; 
25802
25803     /*
25804  * - LGPL
25805  *
25806  * HtmlEditor
25807  * 
25808  */
25809
25810 /**
25811  * @class Roo.bootstrap.HtmlEditor
25812  * @extends Roo.bootstrap.TextArea
25813  * Bootstrap HtmlEditor class
25814
25815  * @constructor
25816  * Create a new HtmlEditor
25817  * @param {Object} config The config object
25818  */
25819
25820 Roo.bootstrap.HtmlEditor = function(config){
25821     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25822     if (!this.toolbars) {
25823         this.toolbars = [];
25824     }
25825     
25826     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25827     this.addEvents({
25828             /**
25829              * @event initialize
25830              * Fires when the editor is fully initialized (including the iframe)
25831              * @param {HtmlEditor} this
25832              */
25833             initialize: true,
25834             /**
25835              * @event activate
25836              * Fires when the editor is first receives the focus. Any insertion must wait
25837              * until after this event.
25838              * @param {HtmlEditor} this
25839              */
25840             activate: true,
25841              /**
25842              * @event beforesync
25843              * Fires before the textarea is updated with content from the editor iframe. Return false
25844              * to cancel the sync.
25845              * @param {HtmlEditor} this
25846              * @param {String} html
25847              */
25848             beforesync: true,
25849              /**
25850              * @event beforepush
25851              * Fires before the iframe editor is updated with content from the textarea. Return false
25852              * to cancel the push.
25853              * @param {HtmlEditor} this
25854              * @param {String} html
25855              */
25856             beforepush: true,
25857              /**
25858              * @event sync
25859              * Fires when the textarea is updated with content from the editor iframe.
25860              * @param {HtmlEditor} this
25861              * @param {String} html
25862              */
25863             sync: true,
25864              /**
25865              * @event push
25866              * Fires when the iframe editor is updated with content from the textarea.
25867              * @param {HtmlEditor} this
25868              * @param {String} html
25869              */
25870             push: true,
25871              /**
25872              * @event editmodechange
25873              * Fires when the editor switches edit modes
25874              * @param {HtmlEditor} this
25875              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25876              */
25877             editmodechange: true,
25878             /**
25879              * @event editorevent
25880              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25881              * @param {HtmlEditor} this
25882              */
25883             editorevent: true,
25884             /**
25885              * @event firstfocus
25886              * Fires when on first focus - needed by toolbars..
25887              * @param {HtmlEditor} this
25888              */
25889             firstfocus: true,
25890             /**
25891              * @event autosave
25892              * Auto save the htmlEditor value as a file into Events
25893              * @param {HtmlEditor} this
25894              */
25895             autosave: true,
25896             /**
25897              * @event savedpreview
25898              * preview the saved version of htmlEditor
25899              * @param {HtmlEditor} this
25900              */
25901             savedpreview: true
25902         });
25903 };
25904
25905
25906 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25907     
25908     
25909       /**
25910      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25911      */
25912     toolbars : false,
25913     
25914      /**
25915     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25916     */
25917     btns : [],
25918    
25919      /**
25920      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25921      *                        Roo.resizable.
25922      */
25923     resizable : false,
25924      /**
25925      * @cfg {Number} height (in pixels)
25926      */   
25927     height: 300,
25928    /**
25929      * @cfg {Number} width (in pixels)
25930      */   
25931     width: false,
25932     
25933     /**
25934      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25935      * 
25936      */
25937     stylesheets: false,
25938     
25939     // id of frame..
25940     frameId: false,
25941     
25942     // private properties
25943     validationEvent : false,
25944     deferHeight: true,
25945     initialized : false,
25946     activated : false,
25947     
25948     onFocus : Roo.emptyFn,
25949     iframePad:3,
25950     hideMode:'offsets',
25951     
25952     tbContainer : false,
25953     
25954     bodyCls : '',
25955     
25956     toolbarContainer :function() {
25957         return this.wrap.select('.x-html-editor-tb',true).first();
25958     },
25959
25960     /**
25961      * Protected method that will not generally be called directly. It
25962      * is called when the editor creates its toolbar. Override this method if you need to
25963      * add custom toolbar buttons.
25964      * @param {HtmlEditor} editor
25965      */
25966     createToolbar : function(){
25967         Roo.log('renewing');
25968         Roo.log("create toolbars");
25969         
25970         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25971         this.toolbars[0].render(this.toolbarContainer());
25972         
25973         return;
25974         
25975 //        if (!editor.toolbars || !editor.toolbars.length) {
25976 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25977 //        }
25978 //        
25979 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25980 //            editor.toolbars[i] = Roo.factory(
25981 //                    typeof(editor.toolbars[i]) == 'string' ?
25982 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25983 //                Roo.bootstrap.HtmlEditor);
25984 //            editor.toolbars[i].init(editor);
25985 //        }
25986     },
25987
25988      
25989     // private
25990     onRender : function(ct, position)
25991     {
25992        // Roo.log("Call onRender: " + this.xtype);
25993         var _t = this;
25994         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25995       
25996         this.wrap = this.inputEl().wrap({
25997             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25998         });
25999         
26000         this.editorcore.onRender(ct, position);
26001          
26002         if (this.resizable) {
26003             this.resizeEl = new Roo.Resizable(this.wrap, {
26004                 pinned : true,
26005                 wrap: true,
26006                 dynamic : true,
26007                 minHeight : this.height,
26008                 height: this.height,
26009                 handles : this.resizable,
26010                 width: this.width,
26011                 listeners : {
26012                     resize : function(r, w, h) {
26013                         _t.onResize(w,h); // -something
26014                     }
26015                 }
26016             });
26017             
26018         }
26019         this.createToolbar(this);
26020        
26021         
26022         if(!this.width && this.resizable){
26023             this.setSize(this.wrap.getSize());
26024         }
26025         if (this.resizeEl) {
26026             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
26027             // should trigger onReize..
26028         }
26029         
26030     },
26031
26032     // private
26033     onResize : function(w, h)
26034     {
26035         Roo.log('resize: ' +w + ',' + h );
26036         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
26037         var ew = false;
26038         var eh = false;
26039         
26040         if(this.inputEl() ){
26041             if(typeof w == 'number'){
26042                 var aw = w - this.wrap.getFrameWidth('lr');
26043                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
26044                 ew = aw;
26045             }
26046             if(typeof h == 'number'){
26047                  var tbh = -11;  // fixme it needs to tool bar size!
26048                 for (var i =0; i < this.toolbars.length;i++) {
26049                     // fixme - ask toolbars for heights?
26050                     tbh += this.toolbars[i].el.getHeight();
26051                     //if (this.toolbars[i].footer) {
26052                     //    tbh += this.toolbars[i].footer.el.getHeight();
26053                     //}
26054                 }
26055               
26056                 
26057                 
26058                 
26059                 
26060                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
26061                 ah -= 5; // knock a few pixes off for look..
26062                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
26063                 var eh = ah;
26064             }
26065         }
26066         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
26067         this.editorcore.onResize(ew,eh);
26068         
26069     },
26070
26071     /**
26072      * Toggles the editor between standard and source edit mode.
26073      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
26074      */
26075     toggleSourceEdit : function(sourceEditMode)
26076     {
26077         this.editorcore.toggleSourceEdit(sourceEditMode);
26078         
26079         if(this.editorcore.sourceEditMode){
26080             Roo.log('editor - showing textarea');
26081             
26082 //            Roo.log('in');
26083 //            Roo.log(this.syncValue());
26084             this.syncValue();
26085             this.inputEl().removeClass(['hide', 'x-hidden']);
26086             this.inputEl().dom.removeAttribute('tabIndex');
26087             this.inputEl().focus();
26088         }else{
26089             Roo.log('editor - hiding textarea');
26090 //            Roo.log('out')
26091 //            Roo.log(this.pushValue()); 
26092             this.pushValue();
26093             
26094             this.inputEl().addClass(['hide', 'x-hidden']);
26095             this.inputEl().dom.setAttribute('tabIndex', -1);
26096             //this.deferFocus();
26097         }
26098          
26099         if(this.resizable){
26100             this.setSize(this.wrap.getSize());
26101         }
26102         
26103         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
26104     },
26105  
26106     // private (for BoxComponent)
26107     adjustSize : Roo.BoxComponent.prototype.adjustSize,
26108
26109     // private (for BoxComponent)
26110     getResizeEl : function(){
26111         return this.wrap;
26112     },
26113
26114     // private (for BoxComponent)
26115     getPositionEl : function(){
26116         return this.wrap;
26117     },
26118
26119     // private
26120     initEvents : function(){
26121         this.originalValue = this.getValue();
26122     },
26123
26124 //    /**
26125 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26126 //     * @method
26127 //     */
26128 //    markInvalid : Roo.emptyFn,
26129 //    /**
26130 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
26131 //     * @method
26132 //     */
26133 //    clearInvalid : Roo.emptyFn,
26134
26135     setValue : function(v){
26136         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26137         this.editorcore.pushValue();
26138     },
26139
26140      
26141     // private
26142     deferFocus : function(){
26143         this.focus.defer(10, this);
26144     },
26145
26146     // doc'ed in Field
26147     focus : function(){
26148         this.editorcore.focus();
26149         
26150     },
26151       
26152
26153     // private
26154     onDestroy : function(){
26155         
26156         
26157         
26158         if(this.rendered){
26159             
26160             for (var i =0; i < this.toolbars.length;i++) {
26161                 // fixme - ask toolbars for heights?
26162                 this.toolbars[i].onDestroy();
26163             }
26164             
26165             this.wrap.dom.innerHTML = '';
26166             this.wrap.remove();
26167         }
26168     },
26169
26170     // private
26171     onFirstFocus : function(){
26172         //Roo.log("onFirstFocus");
26173         this.editorcore.onFirstFocus();
26174          for (var i =0; i < this.toolbars.length;i++) {
26175             this.toolbars[i].onFirstFocus();
26176         }
26177         
26178     },
26179     
26180     // private
26181     syncValue : function()
26182     {   
26183         this.editorcore.syncValue();
26184     },
26185     
26186     pushValue : function()
26187     {   
26188         this.editorcore.pushValue();
26189     }
26190      
26191     
26192     // hide stuff that is not compatible
26193     /**
26194      * @event blur
26195      * @hide
26196      */
26197     /**
26198      * @event change
26199      * @hide
26200      */
26201     /**
26202      * @event focus
26203      * @hide
26204      */
26205     /**
26206      * @event specialkey
26207      * @hide
26208      */
26209     /**
26210      * @cfg {String} fieldClass @hide
26211      */
26212     /**
26213      * @cfg {String} focusClass @hide
26214      */
26215     /**
26216      * @cfg {String} autoCreate @hide
26217      */
26218     /**
26219      * @cfg {String} inputType @hide
26220      */
26221      
26222     /**
26223      * @cfg {String} invalidText @hide
26224      */
26225     /**
26226      * @cfg {String} msgFx @hide
26227      */
26228     /**
26229      * @cfg {String} validateOnBlur @hide
26230      */
26231 });
26232  
26233     
26234    
26235    
26236    
26237       
26238 Roo.namespace('Roo.bootstrap.htmleditor');
26239 /**
26240  * @class Roo.bootstrap.HtmlEditorToolbar1
26241  * Basic Toolbar
26242  * 
26243  * @example
26244  * Usage:
26245  *
26246  new Roo.bootstrap.HtmlEditor({
26247     ....
26248     toolbars : [
26249         new Roo.bootstrap.HtmlEditorToolbar1({
26250             disable : { fonts: 1 , format: 1, ..., ... , ...],
26251             btns : [ .... ]
26252         })
26253     }
26254      
26255  * 
26256  * @cfg {Object} disable List of elements to disable..
26257  * @cfg {Array} btns List of additional buttons.
26258  * 
26259  * 
26260  * NEEDS Extra CSS? 
26261  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26262  */
26263  
26264 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26265 {
26266     
26267     Roo.apply(this, config);
26268     
26269     // default disabled, based on 'good practice'..
26270     this.disable = this.disable || {};
26271     Roo.applyIf(this.disable, {
26272         fontSize : true,
26273         colors : true,
26274         specialElements : true
26275     });
26276     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26277     
26278     this.editor = config.editor;
26279     this.editorcore = config.editor.editorcore;
26280     
26281     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26282     
26283     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26284     // dont call parent... till later.
26285 }
26286 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26287      
26288     bar : true,
26289     
26290     editor : false,
26291     editorcore : false,
26292     
26293     
26294     formats : [
26295         "p" ,  
26296         "h1","h2","h3","h4","h5","h6", 
26297         "pre", "code", 
26298         "abbr", "acronym", "address", "cite", "samp", "var",
26299         'div','span'
26300     ],
26301     
26302     onRender : function(ct, position)
26303     {
26304        // Roo.log("Call onRender: " + this.xtype);
26305         
26306        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26307        Roo.log(this.el);
26308        this.el.dom.style.marginBottom = '0';
26309        var _this = this;
26310        var editorcore = this.editorcore;
26311        var editor= this.editor;
26312        
26313        var children = [];
26314        var btn = function(id,cmd , toggle, handler, html){
26315        
26316             var  event = toggle ? 'toggle' : 'click';
26317        
26318             var a = {
26319                 size : 'sm',
26320                 xtype: 'Button',
26321                 xns: Roo.bootstrap,
26322                 //glyphicon : id,
26323                 fa: id,
26324                 cmd : id || cmd,
26325                 enableToggle:toggle !== false,
26326                 html : html || '',
26327                 pressed : toggle ? false : null,
26328                 listeners : {}
26329             };
26330             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26331                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26332             };
26333             children.push(a);
26334             return a;
26335        }
26336        
26337     //    var cb_box = function...
26338         
26339         var style = {
26340                 xtype: 'Button',
26341                 size : 'sm',
26342                 xns: Roo.bootstrap,
26343                 fa : 'font',
26344                 //html : 'submit'
26345                 menu : {
26346                     xtype: 'Menu',
26347                     xns: Roo.bootstrap,
26348                     items:  []
26349                 }
26350         };
26351         Roo.each(this.formats, function(f) {
26352             style.menu.items.push({
26353                 xtype :'MenuItem',
26354                 xns: Roo.bootstrap,
26355                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26356                 tagname : f,
26357                 listeners : {
26358                     click : function()
26359                     {
26360                         editorcore.insertTag(this.tagname);
26361                         editor.focus();
26362                     }
26363                 }
26364                 
26365             });
26366         });
26367         children.push(style);   
26368         
26369         btn('bold',false,true);
26370         btn('italic',false,true);
26371         btn('align-left', 'justifyleft',true);
26372         btn('align-center', 'justifycenter',true);
26373         btn('align-right' , 'justifyright',true);
26374         btn('link', false, false, function(btn) {
26375             //Roo.log("create link?");
26376             var url = prompt(this.createLinkText, this.defaultLinkValue);
26377             if(url && url != 'http:/'+'/'){
26378                 this.editorcore.relayCmd('createlink', url);
26379             }
26380         }),
26381         btn('list','insertunorderedlist',true);
26382         btn('pencil', false,true, function(btn){
26383                 Roo.log(this);
26384                 this.toggleSourceEdit(btn.pressed);
26385         });
26386         
26387         if (this.editor.btns.length > 0) {
26388             for (var i = 0; i<this.editor.btns.length; i++) {
26389                 children.push(this.editor.btns[i]);
26390             }
26391         }
26392         
26393         /*
26394         var cog = {
26395                 xtype: 'Button',
26396                 size : 'sm',
26397                 xns: Roo.bootstrap,
26398                 glyphicon : 'cog',
26399                 //html : 'submit'
26400                 menu : {
26401                     xtype: 'Menu',
26402                     xns: Roo.bootstrap,
26403                     items:  []
26404                 }
26405         };
26406         
26407         cog.menu.items.push({
26408             xtype :'MenuItem',
26409             xns: Roo.bootstrap,
26410             html : Clean styles,
26411             tagname : f,
26412             listeners : {
26413                 click : function()
26414                 {
26415                     editorcore.insertTag(this.tagname);
26416                     editor.focus();
26417                 }
26418             }
26419             
26420         });
26421        */
26422         
26423          
26424        this.xtype = 'NavSimplebar';
26425         
26426         for(var i=0;i< children.length;i++) {
26427             
26428             this.buttons.add(this.addxtypeChild(children[i]));
26429             
26430         }
26431         
26432         editor.on('editorevent', this.updateToolbar, this);
26433     },
26434     onBtnClick : function(id)
26435     {
26436        this.editorcore.relayCmd(id);
26437        this.editorcore.focus();
26438     },
26439     
26440     /**
26441      * Protected method that will not generally be called directly. It triggers
26442      * a toolbar update by reading the markup state of the current selection in the editor.
26443      */
26444     updateToolbar: function(){
26445
26446         if(!this.editorcore.activated){
26447             this.editor.onFirstFocus(); // is this neeed?
26448             return;
26449         }
26450
26451         var btns = this.buttons; 
26452         var doc = this.editorcore.doc;
26453         btns.get('bold').setActive(doc.queryCommandState('bold'));
26454         btns.get('italic').setActive(doc.queryCommandState('italic'));
26455         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26456         
26457         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26458         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26459         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26460         
26461         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26462         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26463          /*
26464         
26465         var ans = this.editorcore.getAllAncestors();
26466         if (this.formatCombo) {
26467             
26468             
26469             var store = this.formatCombo.store;
26470             this.formatCombo.setValue("");
26471             for (var i =0; i < ans.length;i++) {
26472                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26473                     // select it..
26474                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26475                     break;
26476                 }
26477             }
26478         }
26479         
26480         
26481         
26482         // hides menus... - so this cant be on a menu...
26483         Roo.bootstrap.MenuMgr.hideAll();
26484         */
26485         Roo.bootstrap.MenuMgr.hideAll();
26486         //this.editorsyncValue();
26487     },
26488     onFirstFocus: function() {
26489         this.buttons.each(function(item){
26490            item.enable();
26491         });
26492     },
26493     toggleSourceEdit : function(sourceEditMode){
26494         
26495           
26496         if(sourceEditMode){
26497             Roo.log("disabling buttons");
26498            this.buttons.each( function(item){
26499                 if(item.cmd != 'pencil'){
26500                     item.disable();
26501                 }
26502             });
26503           
26504         }else{
26505             Roo.log("enabling buttons");
26506             if(this.editorcore.initialized){
26507                 this.buttons.each( function(item){
26508                     item.enable();
26509                 });
26510             }
26511             
26512         }
26513         Roo.log("calling toggole on editor");
26514         // tell the editor that it's been pressed..
26515         this.editor.toggleSourceEdit(sourceEditMode);
26516        
26517     }
26518 });
26519
26520
26521
26522
26523  
26524 /*
26525  * - LGPL
26526  */
26527
26528 /**
26529  * @class Roo.bootstrap.Markdown
26530  * @extends Roo.bootstrap.TextArea
26531  * Bootstrap Showdown editable area
26532  * @cfg {string} content
26533  * 
26534  * @constructor
26535  * Create a new Showdown
26536  */
26537
26538 Roo.bootstrap.Markdown = function(config){
26539     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26540    
26541 };
26542
26543 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26544     
26545     editing :false,
26546     
26547     initEvents : function()
26548     {
26549         
26550         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26551         this.markdownEl = this.el.createChild({
26552             cls : 'roo-markdown-area'
26553         });
26554         this.inputEl().addClass('d-none');
26555         if (this.getValue() == '') {
26556             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26557             
26558         } else {
26559             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26560         }
26561         this.markdownEl.on('click', this.toggleTextEdit, this);
26562         this.on('blur', this.toggleTextEdit, this);
26563         this.on('specialkey', this.resizeTextArea, this);
26564     },
26565     
26566     toggleTextEdit : function()
26567     {
26568         var sh = this.markdownEl.getHeight();
26569         this.inputEl().addClass('d-none');
26570         this.markdownEl.addClass('d-none');
26571         if (!this.editing) {
26572             // show editor?
26573             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26574             this.inputEl().removeClass('d-none');
26575             this.inputEl().focus();
26576             this.editing = true;
26577             return;
26578         }
26579         // show showdown...
26580         this.updateMarkdown();
26581         this.markdownEl.removeClass('d-none');
26582         this.editing = false;
26583         return;
26584     },
26585     updateMarkdown : function()
26586     {
26587         if (this.getValue() == '') {
26588             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26589             return;
26590         }
26591  
26592         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26593     },
26594     
26595     resizeTextArea: function () {
26596         
26597         var sh = 100;
26598         Roo.log([sh, this.getValue().split("\n").length * 30]);
26599         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26600     },
26601     setValue : function(val)
26602     {
26603         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26604         if (!this.editing) {
26605             this.updateMarkdown();
26606         }
26607         
26608     },
26609     focus : function()
26610     {
26611         if (!this.editing) {
26612             this.toggleTextEdit();
26613         }
26614         
26615     }
26616
26617
26618 });
26619 /**
26620  * @class Roo.bootstrap.Table.AbstractSelectionModel
26621  * @extends Roo.util.Observable
26622  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26623  * implemented by descendant classes.  This class should not be directly instantiated.
26624  * @constructor
26625  */
26626 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26627     this.locked = false;
26628     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26629 };
26630
26631
26632 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26633     /** @ignore Called by the grid automatically. Do not call directly. */
26634     init : function(grid){
26635         this.grid = grid;
26636         this.initEvents();
26637     },
26638
26639     /**
26640      * Locks the selections.
26641      */
26642     lock : function(){
26643         this.locked = true;
26644     },
26645
26646     /**
26647      * Unlocks the selections.
26648      */
26649     unlock : function(){
26650         this.locked = false;
26651     },
26652
26653     /**
26654      * Returns true if the selections are locked.
26655      * @return {Boolean}
26656      */
26657     isLocked : function(){
26658         return this.locked;
26659     },
26660     
26661     
26662     initEvents : function ()
26663     {
26664         
26665     }
26666 });
26667 /**
26668  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26669  * @class Roo.bootstrap.Table.RowSelectionModel
26670  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26671  * It supports multiple selections and keyboard selection/navigation. 
26672  * @constructor
26673  * @param {Object} config
26674  */
26675
26676 Roo.bootstrap.Table.RowSelectionModel = function(config){
26677     Roo.apply(this, config);
26678     this.selections = new Roo.util.MixedCollection(false, function(o){
26679         return o.id;
26680     });
26681
26682     this.last = false;
26683     this.lastActive = false;
26684
26685     this.addEvents({
26686         /**
26687              * @event selectionchange
26688              * Fires when the selection changes
26689              * @param {SelectionModel} this
26690              */
26691             "selectionchange" : true,
26692         /**
26693              * @event afterselectionchange
26694              * Fires after the selection changes (eg. by key press or clicking)
26695              * @param {SelectionModel} this
26696              */
26697             "afterselectionchange" : true,
26698         /**
26699              * @event beforerowselect
26700              * Fires when a row is selected being selected, return false to cancel.
26701              * @param {SelectionModel} this
26702              * @param {Number} rowIndex The selected index
26703              * @param {Boolean} keepExisting False if other selections will be cleared
26704              */
26705             "beforerowselect" : true,
26706         /**
26707              * @event rowselect
26708              * Fires when a row is selected.
26709              * @param {SelectionModel} this
26710              * @param {Number} rowIndex The selected index
26711              * @param {Roo.data.Record} r The record
26712              */
26713             "rowselect" : true,
26714         /**
26715              * @event rowdeselect
26716              * Fires when a row is deselected.
26717              * @param {SelectionModel} this
26718              * @param {Number} rowIndex The selected index
26719              */
26720         "rowdeselect" : true
26721     });
26722     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26723     this.locked = false;
26724  };
26725
26726 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26727     /**
26728      * @cfg {Boolean} singleSelect
26729      * True to allow selection of only one row at a time (defaults to false)
26730      */
26731     singleSelect : false,
26732
26733     // private
26734     initEvents : function()
26735     {
26736
26737         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26738         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26739         //}else{ // allow click to work like normal
26740          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26741         //}
26742         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26743         this.grid.on("rowclick", this.handleMouseDown, this);
26744         
26745         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26746             "up" : function(e){
26747                 if(!e.shiftKey){
26748                     this.selectPrevious(e.shiftKey);
26749                 }else if(this.last !== false && this.lastActive !== false){
26750                     var last = this.last;
26751                     this.selectRange(this.last,  this.lastActive-1);
26752                     this.grid.getView().focusRow(this.lastActive);
26753                     if(last !== false){
26754                         this.last = last;
26755                     }
26756                 }else{
26757                     this.selectFirstRow();
26758                 }
26759                 this.fireEvent("afterselectionchange", this);
26760             },
26761             "down" : function(e){
26762                 if(!e.shiftKey){
26763                     this.selectNext(e.shiftKey);
26764                 }else if(this.last !== false && this.lastActive !== false){
26765                     var last = this.last;
26766                     this.selectRange(this.last,  this.lastActive+1);
26767                     this.grid.getView().focusRow(this.lastActive);
26768                     if(last !== false){
26769                         this.last = last;
26770                     }
26771                 }else{
26772                     this.selectFirstRow();
26773                 }
26774                 this.fireEvent("afterselectionchange", this);
26775             },
26776             scope: this
26777         });
26778         this.grid.store.on('load', function(){
26779             this.selections.clear();
26780         },this);
26781         /*
26782         var view = this.grid.view;
26783         view.on("refresh", this.onRefresh, this);
26784         view.on("rowupdated", this.onRowUpdated, this);
26785         view.on("rowremoved", this.onRemove, this);
26786         */
26787     },
26788
26789     // private
26790     onRefresh : function()
26791     {
26792         var ds = this.grid.store, i, v = this.grid.view;
26793         var s = this.selections;
26794         s.each(function(r){
26795             if((i = ds.indexOfId(r.id)) != -1){
26796                 v.onRowSelect(i);
26797             }else{
26798                 s.remove(r);
26799             }
26800         });
26801     },
26802
26803     // private
26804     onRemove : function(v, index, r){
26805         this.selections.remove(r);
26806     },
26807
26808     // private
26809     onRowUpdated : function(v, index, r){
26810         if(this.isSelected(r)){
26811             v.onRowSelect(index);
26812         }
26813     },
26814
26815     /**
26816      * Select records.
26817      * @param {Array} records The records to select
26818      * @param {Boolean} keepExisting (optional) True to keep existing selections
26819      */
26820     selectRecords : function(records, keepExisting)
26821     {
26822         if(!keepExisting){
26823             this.clearSelections();
26824         }
26825             var ds = this.grid.store;
26826         for(var i = 0, len = records.length; i < len; i++){
26827             this.selectRow(ds.indexOf(records[i]), true);
26828         }
26829     },
26830
26831     /**
26832      * Gets the number of selected rows.
26833      * @return {Number}
26834      */
26835     getCount : function(){
26836         return this.selections.length;
26837     },
26838
26839     /**
26840      * Selects the first row in the grid.
26841      */
26842     selectFirstRow : function(){
26843         this.selectRow(0);
26844     },
26845
26846     /**
26847      * Select the last row.
26848      * @param {Boolean} keepExisting (optional) True to keep existing selections
26849      */
26850     selectLastRow : function(keepExisting){
26851         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26852         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26853     },
26854
26855     /**
26856      * Selects the row immediately following the last selected row.
26857      * @param {Boolean} keepExisting (optional) True to keep existing selections
26858      */
26859     selectNext : function(keepExisting)
26860     {
26861             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26862             this.selectRow(this.last+1, keepExisting);
26863             this.grid.getView().focusRow(this.last);
26864         }
26865     },
26866
26867     /**
26868      * Selects the row that precedes the last selected row.
26869      * @param {Boolean} keepExisting (optional) True to keep existing selections
26870      */
26871     selectPrevious : function(keepExisting){
26872         if(this.last){
26873             this.selectRow(this.last-1, keepExisting);
26874             this.grid.getView().focusRow(this.last);
26875         }
26876     },
26877
26878     /**
26879      * Returns the selected records
26880      * @return {Array} Array of selected records
26881      */
26882     getSelections : function(){
26883         return [].concat(this.selections.items);
26884     },
26885
26886     /**
26887      * Returns the first selected record.
26888      * @return {Record}
26889      */
26890     getSelected : function(){
26891         return this.selections.itemAt(0);
26892     },
26893
26894
26895     /**
26896      * Clears all selections.
26897      */
26898     clearSelections : function(fast)
26899     {
26900         if(this.locked) {
26901             return;
26902         }
26903         if(fast !== true){
26904                 var ds = this.grid.store;
26905             var s = this.selections;
26906             s.each(function(r){
26907                 this.deselectRow(ds.indexOfId(r.id));
26908             }, this);
26909             s.clear();
26910         }else{
26911             this.selections.clear();
26912         }
26913         this.last = false;
26914     },
26915
26916
26917     /**
26918      * Selects all rows.
26919      */
26920     selectAll : function(){
26921         if(this.locked) {
26922             return;
26923         }
26924         this.selections.clear();
26925         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26926             this.selectRow(i, true);
26927         }
26928     },
26929
26930     /**
26931      * Returns True if there is a selection.
26932      * @return {Boolean}
26933      */
26934     hasSelection : function(){
26935         return this.selections.length > 0;
26936     },
26937
26938     /**
26939      * Returns True if the specified row is selected.
26940      * @param {Number/Record} record The record or index of the record to check
26941      * @return {Boolean}
26942      */
26943     isSelected : function(index){
26944             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26945         return (r && this.selections.key(r.id) ? true : false);
26946     },
26947
26948     /**
26949      * Returns True if the specified record id is selected.
26950      * @param {String} id The id of record to check
26951      * @return {Boolean}
26952      */
26953     isIdSelected : function(id){
26954         return (this.selections.key(id) ? true : false);
26955     },
26956
26957
26958     // private
26959     handleMouseDBClick : function(e, t){
26960         
26961     },
26962     // private
26963     handleMouseDown : function(e, t)
26964     {
26965             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26966         if(this.isLocked() || rowIndex < 0 ){
26967             return;
26968         };
26969         if(e.shiftKey && this.last !== false){
26970             var last = this.last;
26971             this.selectRange(last, rowIndex, e.ctrlKey);
26972             this.last = last; // reset the last
26973             t.focus();
26974     
26975         }else{
26976             var isSelected = this.isSelected(rowIndex);
26977             //Roo.log("select row:" + rowIndex);
26978             if(isSelected){
26979                 this.deselectRow(rowIndex);
26980             } else {
26981                         this.selectRow(rowIndex, true);
26982             }
26983     
26984             /*
26985                 if(e.button !== 0 && isSelected){
26986                 alert('rowIndex 2: ' + rowIndex);
26987                     view.focusRow(rowIndex);
26988                 }else if(e.ctrlKey && isSelected){
26989                     this.deselectRow(rowIndex);
26990                 }else if(!isSelected){
26991                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26992                     view.focusRow(rowIndex);
26993                 }
26994             */
26995         }
26996         this.fireEvent("afterselectionchange", this);
26997     },
26998     // private
26999     handleDragableRowClick :  function(grid, rowIndex, e) 
27000     {
27001         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
27002             this.selectRow(rowIndex, false);
27003             grid.view.focusRow(rowIndex);
27004              this.fireEvent("afterselectionchange", this);
27005         }
27006     },
27007     
27008     /**
27009      * Selects multiple rows.
27010      * @param {Array} rows Array of the indexes of the row to select
27011      * @param {Boolean} keepExisting (optional) True to keep existing selections
27012      */
27013     selectRows : function(rows, keepExisting){
27014         if(!keepExisting){
27015             this.clearSelections();
27016         }
27017         for(var i = 0, len = rows.length; i < len; i++){
27018             this.selectRow(rows[i], true);
27019         }
27020     },
27021
27022     /**
27023      * Selects a range of rows. All rows in between startRow and endRow are also selected.
27024      * @param {Number} startRow The index of the first row in the range
27025      * @param {Number} endRow The index of the last row in the range
27026      * @param {Boolean} keepExisting (optional) True to retain existing selections
27027      */
27028     selectRange : function(startRow, endRow, keepExisting){
27029         if(this.locked) {
27030             return;
27031         }
27032         if(!keepExisting){
27033             this.clearSelections();
27034         }
27035         if(startRow <= endRow){
27036             for(var i = startRow; i <= endRow; i++){
27037                 this.selectRow(i, true);
27038             }
27039         }else{
27040             for(var i = startRow; i >= endRow; i--){
27041                 this.selectRow(i, true);
27042             }
27043         }
27044     },
27045
27046     /**
27047      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
27048      * @param {Number} startRow The index of the first row in the range
27049      * @param {Number} endRow The index of the last row in the range
27050      */
27051     deselectRange : function(startRow, endRow, preventViewNotify){
27052         if(this.locked) {
27053             return;
27054         }
27055         for(var i = startRow; i <= endRow; i++){
27056             this.deselectRow(i, preventViewNotify);
27057         }
27058     },
27059
27060     /**
27061      * Selects a row.
27062      * @param {Number} row The index of the row to select
27063      * @param {Boolean} keepExisting (optional) True to keep existing selections
27064      */
27065     selectRow : function(index, keepExisting, preventViewNotify)
27066     {
27067             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
27068             return;
27069         }
27070         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
27071             if(!keepExisting || this.singleSelect){
27072                 this.clearSelections();
27073             }
27074             
27075             var r = this.grid.store.getAt(index);
27076             //console.log('selectRow - record id :' + r.id);
27077             
27078             this.selections.add(r);
27079             this.last = this.lastActive = index;
27080             if(!preventViewNotify){
27081                 var proxy = new Roo.Element(
27082                                 this.grid.getRowDom(index)
27083                 );
27084                 proxy.addClass('bg-info info');
27085             }
27086             this.fireEvent("rowselect", this, index, r);
27087             this.fireEvent("selectionchange", this);
27088         }
27089     },
27090
27091     /**
27092      * Deselects a row.
27093      * @param {Number} row The index of the row to deselect
27094      */
27095     deselectRow : function(index, preventViewNotify)
27096     {
27097         if(this.locked) {
27098             return;
27099         }
27100         if(this.last == index){
27101             this.last = false;
27102         }
27103         if(this.lastActive == index){
27104             this.lastActive = false;
27105         }
27106         
27107         var r = this.grid.store.getAt(index);
27108         if (!r) {
27109             return;
27110         }
27111         
27112         this.selections.remove(r);
27113         //.console.log('deselectRow - record id :' + r.id);
27114         if(!preventViewNotify){
27115         
27116             var proxy = new Roo.Element(
27117                 this.grid.getRowDom(index)
27118             );
27119             proxy.removeClass('bg-info info');
27120         }
27121         this.fireEvent("rowdeselect", this, index);
27122         this.fireEvent("selectionchange", this);
27123     },
27124
27125     // private
27126     restoreLast : function(){
27127         if(this._last){
27128             this.last = this._last;
27129         }
27130     },
27131
27132     // private
27133     acceptsNav : function(row, col, cm){
27134         return !cm.isHidden(col) && cm.isCellEditable(col, row);
27135     },
27136
27137     // private
27138     onEditorKey : function(field, e){
27139         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27140         if(k == e.TAB){
27141             e.stopEvent();
27142             ed.completeEdit();
27143             if(e.shiftKey){
27144                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27145             }else{
27146                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27147             }
27148         }else if(k == e.ENTER && !e.ctrlKey){
27149             e.stopEvent();
27150             ed.completeEdit();
27151             if(e.shiftKey){
27152                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27153             }else{
27154                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27155             }
27156         }else if(k == e.ESC){
27157             ed.cancelEdit();
27158         }
27159         if(newCell){
27160             g.startEditing(newCell[0], newCell[1]);
27161         }
27162     }
27163 });
27164 /*
27165  * Based on:
27166  * Ext JS Library 1.1.1
27167  * Copyright(c) 2006-2007, Ext JS, LLC.
27168  *
27169  * Originally Released Under LGPL - original licence link has changed is not relivant.
27170  *
27171  * Fork - LGPL
27172  * <script type="text/javascript">
27173  */
27174  
27175 /**
27176  * @class Roo.bootstrap.PagingToolbar
27177  * @extends Roo.bootstrap.NavSimplebar
27178  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27179  * @constructor
27180  * Create a new PagingToolbar
27181  * @param {Object} config The config object
27182  * @param {Roo.data.Store} store
27183  */
27184 Roo.bootstrap.PagingToolbar = function(config)
27185 {
27186     // old args format still supported... - xtype is prefered..
27187         // created from xtype...
27188     
27189     this.ds = config.dataSource;
27190     
27191     if (config.store && !this.ds) {
27192         this.store= Roo.factory(config.store, Roo.data);
27193         this.ds = this.store;
27194         this.ds.xmodule = this.xmodule || false;
27195     }
27196     
27197     this.toolbarItems = [];
27198     if (config.items) {
27199         this.toolbarItems = config.items;
27200     }
27201     
27202     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27203     
27204     this.cursor = 0;
27205     
27206     if (this.ds) { 
27207         this.bind(this.ds);
27208     }
27209     
27210     if (Roo.bootstrap.version == 4) {
27211         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27212     } else {
27213         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27214     }
27215     
27216 };
27217
27218 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27219     /**
27220      * @cfg {Roo.data.Store} dataSource
27221      * The underlying data store providing the paged data
27222      */
27223     /**
27224      * @cfg {String/HTMLElement/Element} container
27225      * container The id or element that will contain the toolbar
27226      */
27227     /**
27228      * @cfg {Boolean} displayInfo
27229      * True to display the displayMsg (defaults to false)
27230      */
27231     /**
27232      * @cfg {Number} pageSize
27233      * The number of records to display per page (defaults to 20)
27234      */
27235     pageSize: 20,
27236     /**
27237      * @cfg {String} displayMsg
27238      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27239      */
27240     displayMsg : 'Displaying {0} - {1} of {2}',
27241     /**
27242      * @cfg {String} emptyMsg
27243      * The message to display when no records are found (defaults to "No data to display")
27244      */
27245     emptyMsg : 'No data to display',
27246     /**
27247      * Customizable piece of the default paging text (defaults to "Page")
27248      * @type String
27249      */
27250     beforePageText : "Page",
27251     /**
27252      * Customizable piece of the default paging text (defaults to "of %0")
27253      * @type String
27254      */
27255     afterPageText : "of {0}",
27256     /**
27257      * Customizable piece of the default paging text (defaults to "First Page")
27258      * @type String
27259      */
27260     firstText : "First Page",
27261     /**
27262      * Customizable piece of the default paging text (defaults to "Previous Page")
27263      * @type String
27264      */
27265     prevText : "Previous Page",
27266     /**
27267      * Customizable piece of the default paging text (defaults to "Next Page")
27268      * @type String
27269      */
27270     nextText : "Next Page",
27271     /**
27272      * Customizable piece of the default paging text (defaults to "Last Page")
27273      * @type String
27274      */
27275     lastText : "Last Page",
27276     /**
27277      * Customizable piece of the default paging text (defaults to "Refresh")
27278      * @type String
27279      */
27280     refreshText : "Refresh",
27281
27282     buttons : false,
27283     // private
27284     onRender : function(ct, position) 
27285     {
27286         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27287         this.navgroup.parentId = this.id;
27288         this.navgroup.onRender(this.el, null);
27289         // add the buttons to the navgroup
27290         
27291         if(this.displayInfo){
27292             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27293             this.displayEl = this.el.select('.x-paging-info', true).first();
27294 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27295 //            this.displayEl = navel.el.select('span',true).first();
27296         }
27297         
27298         var _this = this;
27299         
27300         if(this.buttons){
27301             Roo.each(_this.buttons, function(e){ // this might need to use render????
27302                Roo.factory(e).render(_this.el);
27303             });
27304         }
27305             
27306         Roo.each(_this.toolbarItems, function(e) {
27307             _this.navgroup.addItem(e);
27308         });
27309         
27310         
27311         this.first = this.navgroup.addItem({
27312             tooltip: this.firstText,
27313             cls: "prev btn-outline-secondary",
27314             html : ' <i class="fa fa-step-backward"></i>',
27315             disabled: true,
27316             preventDefault: true,
27317             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27318         });
27319         
27320         this.prev =  this.navgroup.addItem({
27321             tooltip: this.prevText,
27322             cls: "prev btn-outline-secondary",
27323             html : ' <i class="fa fa-backward"></i>',
27324             disabled: true,
27325             preventDefault: true,
27326             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27327         });
27328     //this.addSeparator();
27329         
27330         
27331         var field = this.navgroup.addItem( {
27332             tagtype : 'span',
27333             cls : 'x-paging-position  btn-outline-secondary',
27334              disabled: true,
27335             html : this.beforePageText  +
27336                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27337                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27338          } ); //?? escaped?
27339         
27340         this.field = field.el.select('input', true).first();
27341         this.field.on("keydown", this.onPagingKeydown, this);
27342         this.field.on("focus", function(){this.dom.select();});
27343     
27344     
27345         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27346         //this.field.setHeight(18);
27347         //this.addSeparator();
27348         this.next = this.navgroup.addItem({
27349             tooltip: this.nextText,
27350             cls: "next btn-outline-secondary",
27351             html : ' <i class="fa fa-forward"></i>',
27352             disabled: true,
27353             preventDefault: true,
27354             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27355         });
27356         this.last = this.navgroup.addItem({
27357             tooltip: this.lastText,
27358             html : ' <i class="fa fa-step-forward"></i>',
27359             cls: "next btn-outline-secondary",
27360             disabled: true,
27361             preventDefault: true,
27362             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27363         });
27364     //this.addSeparator();
27365         this.loading = this.navgroup.addItem({
27366             tooltip: this.refreshText,
27367             cls: "btn-outline-secondary",
27368             html : ' <i class="fa fa-refresh"></i>',
27369             preventDefault: true,
27370             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27371         });
27372         
27373     },
27374
27375     // private
27376     updateInfo : function(){
27377         if(this.displayEl){
27378             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27379             var msg = count == 0 ?
27380                 this.emptyMsg :
27381                 String.format(
27382                     this.displayMsg,
27383                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27384                 );
27385             this.displayEl.update(msg);
27386         }
27387     },
27388
27389     // private
27390     onLoad : function(ds, r, o)
27391     {
27392         this.cursor = o.params && o.params.start ? o.params.start : 0;
27393         
27394         var d = this.getPageData(),
27395             ap = d.activePage,
27396             ps = d.pages;
27397         
27398         
27399         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27400         this.field.dom.value = ap;
27401         this.first.setDisabled(ap == 1);
27402         this.prev.setDisabled(ap == 1);
27403         this.next.setDisabled(ap == ps);
27404         this.last.setDisabled(ap == ps);
27405         this.loading.enable();
27406         this.updateInfo();
27407     },
27408
27409     // private
27410     getPageData : function(){
27411         var total = this.ds.getTotalCount();
27412         return {
27413             total : total,
27414             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27415             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27416         };
27417     },
27418
27419     // private
27420     onLoadError : function(){
27421         this.loading.enable();
27422     },
27423
27424     // private
27425     onPagingKeydown : function(e){
27426         var k = e.getKey();
27427         var d = this.getPageData();
27428         if(k == e.RETURN){
27429             var v = this.field.dom.value, pageNum;
27430             if(!v || isNaN(pageNum = parseInt(v, 10))){
27431                 this.field.dom.value = d.activePage;
27432                 return;
27433             }
27434             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27435             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27436             e.stopEvent();
27437         }
27438         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))
27439         {
27440           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27441           this.field.dom.value = pageNum;
27442           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27443           e.stopEvent();
27444         }
27445         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27446         {
27447           var v = this.field.dom.value, pageNum; 
27448           var increment = (e.shiftKey) ? 10 : 1;
27449           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27450                 increment *= -1;
27451           }
27452           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27453             this.field.dom.value = d.activePage;
27454             return;
27455           }
27456           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27457           {
27458             this.field.dom.value = parseInt(v, 10) + increment;
27459             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27460             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27461           }
27462           e.stopEvent();
27463         }
27464     },
27465
27466     // private
27467     beforeLoad : function(){
27468         if(this.loading){
27469             this.loading.disable();
27470         }
27471     },
27472
27473     // private
27474     onClick : function(which){
27475         
27476         var ds = this.ds;
27477         if (!ds) {
27478             return;
27479         }
27480         
27481         switch(which){
27482             case "first":
27483                 ds.load({params:{start: 0, limit: this.pageSize}});
27484             break;
27485             case "prev":
27486                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27487             break;
27488             case "next":
27489                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27490             break;
27491             case "last":
27492                 var total = ds.getTotalCount();
27493                 var extra = total % this.pageSize;
27494                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27495                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27496             break;
27497             case "refresh":
27498                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27499             break;
27500         }
27501     },
27502
27503     /**
27504      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27505      * @param {Roo.data.Store} store The data store to unbind
27506      */
27507     unbind : function(ds){
27508         ds.un("beforeload", this.beforeLoad, this);
27509         ds.un("load", this.onLoad, this);
27510         ds.un("loadexception", this.onLoadError, this);
27511         ds.un("remove", this.updateInfo, this);
27512         ds.un("add", this.updateInfo, this);
27513         this.ds = undefined;
27514     },
27515
27516     /**
27517      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27518      * @param {Roo.data.Store} store The data store to bind
27519      */
27520     bind : function(ds){
27521         ds.on("beforeload", this.beforeLoad, this);
27522         ds.on("load", this.onLoad, this);
27523         ds.on("loadexception", this.onLoadError, this);
27524         ds.on("remove", this.updateInfo, this);
27525         ds.on("add", this.updateInfo, this);
27526         this.ds = ds;
27527     }
27528 });/*
27529  * - LGPL
27530  *
27531  * element
27532  * 
27533  */
27534
27535 /**
27536  * @class Roo.bootstrap.MessageBar
27537  * @extends Roo.bootstrap.Component
27538  * Bootstrap MessageBar class
27539  * @cfg {String} html contents of the MessageBar
27540  * @cfg {String} weight (info | success | warning | danger) default info
27541  * @cfg {String} beforeClass insert the bar before the given class
27542  * @cfg {Boolean} closable (true | false) default false
27543  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27544  * 
27545  * @constructor
27546  * Create a new Element
27547  * @param {Object} config The config object
27548  */
27549
27550 Roo.bootstrap.MessageBar = function(config){
27551     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27552 };
27553
27554 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27555     
27556     html: '',
27557     weight: 'info',
27558     closable: false,
27559     fixed: false,
27560     beforeClass: 'bootstrap-sticky-wrap',
27561     
27562     getAutoCreate : function(){
27563         
27564         var cfg = {
27565             tag: 'div',
27566             cls: 'alert alert-dismissable alert-' + this.weight,
27567             cn: [
27568                 {
27569                     tag: 'span',
27570                     cls: 'message',
27571                     html: this.html || ''
27572                 }
27573             ]
27574         };
27575         
27576         if(this.fixed){
27577             cfg.cls += ' alert-messages-fixed';
27578         }
27579         
27580         if(this.closable){
27581             cfg.cn.push({
27582                 tag: 'button',
27583                 cls: 'close',
27584                 html: 'x'
27585             });
27586         }
27587         
27588         return cfg;
27589     },
27590     
27591     onRender : function(ct, position)
27592     {
27593         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27594         
27595         if(!this.el){
27596             var cfg = Roo.apply({},  this.getAutoCreate());
27597             cfg.id = Roo.id();
27598             
27599             if (this.cls) {
27600                 cfg.cls += ' ' + this.cls;
27601             }
27602             if (this.style) {
27603                 cfg.style = this.style;
27604             }
27605             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27606             
27607             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27608         }
27609         
27610         this.el.select('>button.close').on('click', this.hide, this);
27611         
27612     },
27613     
27614     show : function()
27615     {
27616         if (!this.rendered) {
27617             this.render();
27618         }
27619         
27620         this.el.show();
27621         
27622         this.fireEvent('show', this);
27623         
27624     },
27625     
27626     hide : function()
27627     {
27628         if (!this.rendered) {
27629             this.render();
27630         }
27631         
27632         this.el.hide();
27633         
27634         this.fireEvent('hide', this);
27635     },
27636     
27637     update : function()
27638     {
27639 //        var e = this.el.dom.firstChild;
27640 //        
27641 //        if(this.closable){
27642 //            e = e.nextSibling;
27643 //        }
27644 //        
27645 //        e.data = this.html || '';
27646
27647         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27648     }
27649    
27650 });
27651
27652  
27653
27654      /*
27655  * - LGPL
27656  *
27657  * Graph
27658  * 
27659  */
27660
27661
27662 /**
27663  * @class Roo.bootstrap.Graph
27664  * @extends Roo.bootstrap.Component
27665  * Bootstrap Graph class
27666 > Prameters
27667  -sm {number} sm 4
27668  -md {number} md 5
27669  @cfg {String} graphtype  bar | vbar | pie
27670  @cfg {number} g_x coodinator | centre x (pie)
27671  @cfg {number} g_y coodinator | centre y (pie)
27672  @cfg {number} g_r radius (pie)
27673  @cfg {number} g_height height of the chart (respected by all elements in the set)
27674  @cfg {number} g_width width of the chart (respected by all elements in the set)
27675  @cfg {Object} title The title of the chart
27676     
27677  -{Array}  values
27678  -opts (object) options for the chart 
27679      o {
27680      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27681      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27682      o vgutter (number)
27683      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.
27684      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27685      o to
27686      o stretch (boolean)
27687      o }
27688  -opts (object) options for the pie
27689      o{
27690      o cut
27691      o startAngle (number)
27692      o endAngle (number)
27693      } 
27694  *
27695  * @constructor
27696  * Create a new Input
27697  * @param {Object} config The config object
27698  */
27699
27700 Roo.bootstrap.Graph = function(config){
27701     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27702     
27703     this.addEvents({
27704         // img events
27705         /**
27706          * @event click
27707          * The img click event for the img.
27708          * @param {Roo.EventObject} e
27709          */
27710         "click" : true
27711     });
27712 };
27713
27714 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27715     
27716     sm: 4,
27717     md: 5,
27718     graphtype: 'bar',
27719     g_height: 250,
27720     g_width: 400,
27721     g_x: 50,
27722     g_y: 50,
27723     g_r: 30,
27724     opts:{
27725         //g_colors: this.colors,
27726         g_type: 'soft',
27727         g_gutter: '20%'
27728
27729     },
27730     title : false,
27731
27732     getAutoCreate : function(){
27733         
27734         var cfg = {
27735             tag: 'div',
27736             html : null
27737         };
27738         
27739         
27740         return  cfg;
27741     },
27742
27743     onRender : function(ct,position){
27744         
27745         
27746         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27747         
27748         if (typeof(Raphael) == 'undefined') {
27749             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27750             return;
27751         }
27752         
27753         this.raphael = Raphael(this.el.dom);
27754         
27755                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27756                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27757                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27758                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27759                 /*
27760                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27761                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27762                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27763                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27764                 
27765                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27766                 r.barchart(330, 10, 300, 220, data1);
27767                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27768                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27769                 */
27770                 
27771                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27772                 // r.barchart(30, 30, 560, 250,  xdata, {
27773                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27774                 //     axis : "0 0 1 1",
27775                 //     axisxlabels :  xdata
27776                 //     //yvalues : cols,
27777                    
27778                 // });
27779 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27780 //        
27781 //        this.load(null,xdata,{
27782 //                axis : "0 0 1 1",
27783 //                axisxlabels :  xdata
27784 //                });
27785
27786     },
27787
27788     load : function(graphtype,xdata,opts)
27789     {
27790         this.raphael.clear();
27791         if(!graphtype) {
27792             graphtype = this.graphtype;
27793         }
27794         if(!opts){
27795             opts = this.opts;
27796         }
27797         var r = this.raphael,
27798             fin = function () {
27799                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27800             },
27801             fout = function () {
27802                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27803             },
27804             pfin = function() {
27805                 this.sector.stop();
27806                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27807
27808                 if (this.label) {
27809                     this.label[0].stop();
27810                     this.label[0].attr({ r: 7.5 });
27811                     this.label[1].attr({ "font-weight": 800 });
27812                 }
27813             },
27814             pfout = function() {
27815                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27816
27817                 if (this.label) {
27818                     this.label[0].animate({ r: 5 }, 500, "bounce");
27819                     this.label[1].attr({ "font-weight": 400 });
27820                 }
27821             };
27822
27823         switch(graphtype){
27824             case 'bar':
27825                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27826                 break;
27827             case 'hbar':
27828                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27829                 break;
27830             case 'pie':
27831 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27832 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27833 //            
27834                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27835                 
27836                 break;
27837
27838         }
27839         
27840         if(this.title){
27841             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27842         }
27843         
27844     },
27845     
27846     setTitle: function(o)
27847     {
27848         this.title = o;
27849     },
27850     
27851     initEvents: function() {
27852         
27853         if(!this.href){
27854             this.el.on('click', this.onClick, this);
27855         }
27856     },
27857     
27858     onClick : function(e)
27859     {
27860         Roo.log('img onclick');
27861         this.fireEvent('click', this, e);
27862     }
27863    
27864 });
27865
27866  
27867 /*
27868  * - LGPL
27869  *
27870  * numberBox
27871  * 
27872  */
27873 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27874
27875 /**
27876  * @class Roo.bootstrap.dash.NumberBox
27877  * @extends Roo.bootstrap.Component
27878  * Bootstrap NumberBox class
27879  * @cfg {String} headline Box headline
27880  * @cfg {String} content Box content
27881  * @cfg {String} icon Box icon
27882  * @cfg {String} footer Footer text
27883  * @cfg {String} fhref Footer href
27884  * 
27885  * @constructor
27886  * Create a new NumberBox
27887  * @param {Object} config The config object
27888  */
27889
27890
27891 Roo.bootstrap.dash.NumberBox = function(config){
27892     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27893     
27894 };
27895
27896 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27897     
27898     headline : '',
27899     content : '',
27900     icon : '',
27901     footer : '',
27902     fhref : '',
27903     ficon : '',
27904     
27905     getAutoCreate : function(){
27906         
27907         var cfg = {
27908             tag : 'div',
27909             cls : 'small-box ',
27910             cn : [
27911                 {
27912                     tag : 'div',
27913                     cls : 'inner',
27914                     cn :[
27915                         {
27916                             tag : 'h3',
27917                             cls : 'roo-headline',
27918                             html : this.headline
27919                         },
27920                         {
27921                             tag : 'p',
27922                             cls : 'roo-content',
27923                             html : this.content
27924                         }
27925                     ]
27926                 }
27927             ]
27928         };
27929         
27930         if(this.icon){
27931             cfg.cn.push({
27932                 tag : 'div',
27933                 cls : 'icon',
27934                 cn :[
27935                     {
27936                         tag : 'i',
27937                         cls : 'ion ' + this.icon
27938                     }
27939                 ]
27940             });
27941         }
27942         
27943         if(this.footer){
27944             var footer = {
27945                 tag : 'a',
27946                 cls : 'small-box-footer',
27947                 href : this.fhref || '#',
27948                 html : this.footer
27949             };
27950             
27951             cfg.cn.push(footer);
27952             
27953         }
27954         
27955         return  cfg;
27956     },
27957
27958     onRender : function(ct,position){
27959         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27960
27961
27962        
27963                 
27964     },
27965
27966     setHeadline: function (value)
27967     {
27968         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27969     },
27970     
27971     setFooter: function (value, href)
27972     {
27973         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27974         
27975         if(href){
27976             this.el.select('a.small-box-footer',true).first().attr('href', href);
27977         }
27978         
27979     },
27980
27981     setContent: function (value)
27982     {
27983         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27984     },
27985
27986     initEvents: function() 
27987     {   
27988         
27989     }
27990     
27991 });
27992
27993  
27994 /*
27995  * - LGPL
27996  *
27997  * TabBox
27998  * 
27999  */
28000 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28001
28002 /**
28003  * @class Roo.bootstrap.dash.TabBox
28004  * @extends Roo.bootstrap.Component
28005  * Bootstrap TabBox class
28006  * @cfg {String} title Title of the TabBox
28007  * @cfg {String} icon Icon of the TabBox
28008  * @cfg {Boolean} showtabs (true|false) show the tabs default true
28009  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
28010  * 
28011  * @constructor
28012  * Create a new TabBox
28013  * @param {Object} config The config object
28014  */
28015
28016
28017 Roo.bootstrap.dash.TabBox = function(config){
28018     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
28019     this.addEvents({
28020         // raw events
28021         /**
28022          * @event addpane
28023          * When a pane is added
28024          * @param {Roo.bootstrap.dash.TabPane} pane
28025          */
28026         "addpane" : true,
28027         /**
28028          * @event activatepane
28029          * When a pane is activated
28030          * @param {Roo.bootstrap.dash.TabPane} pane
28031          */
28032         "activatepane" : true
28033         
28034          
28035     });
28036     
28037     this.panes = [];
28038 };
28039
28040 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
28041
28042     title : '',
28043     icon : false,
28044     showtabs : true,
28045     tabScrollable : false,
28046     
28047     getChildContainer : function()
28048     {
28049         return this.el.select('.tab-content', true).first();
28050     },
28051     
28052     getAutoCreate : function(){
28053         
28054         var header = {
28055             tag: 'li',
28056             cls: 'pull-left header',
28057             html: this.title,
28058             cn : []
28059         };
28060         
28061         if(this.icon){
28062             header.cn.push({
28063                 tag: 'i',
28064                 cls: 'fa ' + this.icon
28065             });
28066         }
28067         
28068         var h = {
28069             tag: 'ul',
28070             cls: 'nav nav-tabs pull-right',
28071             cn: [
28072                 header
28073             ]
28074         };
28075         
28076         if(this.tabScrollable){
28077             h = {
28078                 tag: 'div',
28079                 cls: 'tab-header',
28080                 cn: [
28081                     {
28082                         tag: 'ul',
28083                         cls: 'nav nav-tabs pull-right',
28084                         cn: [
28085                             header
28086                         ]
28087                     }
28088                 ]
28089             };
28090         }
28091         
28092         var cfg = {
28093             tag: 'div',
28094             cls: 'nav-tabs-custom',
28095             cn: [
28096                 h,
28097                 {
28098                     tag: 'div',
28099                     cls: 'tab-content no-padding',
28100                     cn: []
28101                 }
28102             ]
28103         };
28104
28105         return  cfg;
28106     },
28107     initEvents : function()
28108     {
28109         //Roo.log('add add pane handler');
28110         this.on('addpane', this.onAddPane, this);
28111     },
28112      /**
28113      * Updates the box title
28114      * @param {String} html to set the title to.
28115      */
28116     setTitle : function(value)
28117     {
28118         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
28119     },
28120     onAddPane : function(pane)
28121     {
28122         this.panes.push(pane);
28123         //Roo.log('addpane');
28124         //Roo.log(pane);
28125         // tabs are rendere left to right..
28126         if(!this.showtabs){
28127             return;
28128         }
28129         
28130         var ctr = this.el.select('.nav-tabs', true).first();
28131          
28132          
28133         var existing = ctr.select('.nav-tab',true);
28134         var qty = existing.getCount();;
28135         
28136         
28137         var tab = ctr.createChild({
28138             tag : 'li',
28139             cls : 'nav-tab' + (qty ? '' : ' active'),
28140             cn : [
28141                 {
28142                     tag : 'a',
28143                     href:'#',
28144                     html : pane.title
28145                 }
28146             ]
28147         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28148         pane.tab = tab;
28149         
28150         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28151         if (!qty) {
28152             pane.el.addClass('active');
28153         }
28154         
28155                 
28156     },
28157     onTabClick : function(ev,un,ob,pane)
28158     {
28159         //Roo.log('tab - prev default');
28160         ev.preventDefault();
28161         
28162         
28163         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28164         pane.tab.addClass('active');
28165         //Roo.log(pane.title);
28166         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28167         // technically we should have a deactivate event.. but maybe add later.
28168         // and it should not de-activate the selected tab...
28169         this.fireEvent('activatepane', pane);
28170         pane.el.addClass('active');
28171         pane.fireEvent('activate');
28172         
28173         
28174     },
28175     
28176     getActivePane : function()
28177     {
28178         var r = false;
28179         Roo.each(this.panes, function(p) {
28180             if(p.el.hasClass('active')){
28181                 r = p;
28182                 return false;
28183             }
28184             
28185             return;
28186         });
28187         
28188         return r;
28189     }
28190     
28191     
28192 });
28193
28194  
28195 /*
28196  * - LGPL
28197  *
28198  * Tab pane
28199  * 
28200  */
28201 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28202 /**
28203  * @class Roo.bootstrap.TabPane
28204  * @extends Roo.bootstrap.Component
28205  * Bootstrap TabPane class
28206  * @cfg {Boolean} active (false | true) Default false
28207  * @cfg {String} title title of panel
28208
28209  * 
28210  * @constructor
28211  * Create a new TabPane
28212  * @param {Object} config The config object
28213  */
28214
28215 Roo.bootstrap.dash.TabPane = function(config){
28216     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28217     
28218     this.addEvents({
28219         // raw events
28220         /**
28221          * @event activate
28222          * When a pane is activated
28223          * @param {Roo.bootstrap.dash.TabPane} pane
28224          */
28225         "activate" : true
28226          
28227     });
28228 };
28229
28230 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28231     
28232     active : false,
28233     title : '',
28234     
28235     // the tabBox that this is attached to.
28236     tab : false,
28237      
28238     getAutoCreate : function() 
28239     {
28240         var cfg = {
28241             tag: 'div',
28242             cls: 'tab-pane'
28243         };
28244         
28245         if(this.active){
28246             cfg.cls += ' active';
28247         }
28248         
28249         return cfg;
28250     },
28251     initEvents  : function()
28252     {
28253         //Roo.log('trigger add pane handler');
28254         this.parent().fireEvent('addpane', this)
28255     },
28256     
28257      /**
28258      * Updates the tab title 
28259      * @param {String} html to set the title to.
28260      */
28261     setTitle: function(str)
28262     {
28263         if (!this.tab) {
28264             return;
28265         }
28266         this.title = str;
28267         this.tab.select('a', true).first().dom.innerHTML = str;
28268         
28269     }
28270     
28271     
28272     
28273 });
28274
28275  
28276
28277
28278  /*
28279  * - LGPL
28280  *
28281  * menu
28282  * 
28283  */
28284 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28285
28286 /**
28287  * @class Roo.bootstrap.menu.Menu
28288  * @extends Roo.bootstrap.Component
28289  * Bootstrap Menu class - container for Menu
28290  * @cfg {String} html Text of the menu
28291  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28292  * @cfg {String} icon Font awesome icon
28293  * @cfg {String} pos Menu align to (top | bottom) default bottom
28294  * 
28295  * 
28296  * @constructor
28297  * Create a new Menu
28298  * @param {Object} config The config object
28299  */
28300
28301
28302 Roo.bootstrap.menu.Menu = function(config){
28303     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28304     
28305     this.addEvents({
28306         /**
28307          * @event beforeshow
28308          * Fires before this menu is displayed
28309          * @param {Roo.bootstrap.menu.Menu} this
28310          */
28311         beforeshow : true,
28312         /**
28313          * @event beforehide
28314          * Fires before this menu is hidden
28315          * @param {Roo.bootstrap.menu.Menu} this
28316          */
28317         beforehide : true,
28318         /**
28319          * @event show
28320          * Fires after this menu is displayed
28321          * @param {Roo.bootstrap.menu.Menu} this
28322          */
28323         show : true,
28324         /**
28325          * @event hide
28326          * Fires after this menu is hidden
28327          * @param {Roo.bootstrap.menu.Menu} this
28328          */
28329         hide : true,
28330         /**
28331          * @event click
28332          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28333          * @param {Roo.bootstrap.menu.Menu} this
28334          * @param {Roo.EventObject} e
28335          */
28336         click : true
28337     });
28338     
28339 };
28340
28341 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28342     
28343     submenu : false,
28344     html : '',
28345     weight : 'default',
28346     icon : false,
28347     pos : 'bottom',
28348     
28349     
28350     getChildContainer : function() {
28351         if(this.isSubMenu){
28352             return this.el;
28353         }
28354         
28355         return this.el.select('ul.dropdown-menu', true).first();  
28356     },
28357     
28358     getAutoCreate : function()
28359     {
28360         var text = [
28361             {
28362                 tag : 'span',
28363                 cls : 'roo-menu-text',
28364                 html : this.html
28365             }
28366         ];
28367         
28368         if(this.icon){
28369             text.unshift({
28370                 tag : 'i',
28371                 cls : 'fa ' + this.icon
28372             })
28373         }
28374         
28375         
28376         var cfg = {
28377             tag : 'div',
28378             cls : 'btn-group',
28379             cn : [
28380                 {
28381                     tag : 'button',
28382                     cls : 'dropdown-button btn btn-' + this.weight,
28383                     cn : text
28384                 },
28385                 {
28386                     tag : 'button',
28387                     cls : 'dropdown-toggle btn btn-' + this.weight,
28388                     cn : [
28389                         {
28390                             tag : 'span',
28391                             cls : 'caret'
28392                         }
28393                     ]
28394                 },
28395                 {
28396                     tag : 'ul',
28397                     cls : 'dropdown-menu'
28398                 }
28399             ]
28400             
28401         };
28402         
28403         if(this.pos == 'top'){
28404             cfg.cls += ' dropup';
28405         }
28406         
28407         if(this.isSubMenu){
28408             cfg = {
28409                 tag : 'ul',
28410                 cls : 'dropdown-menu'
28411             }
28412         }
28413         
28414         return cfg;
28415     },
28416     
28417     onRender : function(ct, position)
28418     {
28419         this.isSubMenu = ct.hasClass('dropdown-submenu');
28420         
28421         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28422     },
28423     
28424     initEvents : function() 
28425     {
28426         if(this.isSubMenu){
28427             return;
28428         }
28429         
28430         this.hidden = true;
28431         
28432         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28433         this.triggerEl.on('click', this.onTriggerPress, this);
28434         
28435         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28436         this.buttonEl.on('click', this.onClick, this);
28437         
28438     },
28439     
28440     list : function()
28441     {
28442         if(this.isSubMenu){
28443             return this.el;
28444         }
28445         
28446         return this.el.select('ul.dropdown-menu', true).first();
28447     },
28448     
28449     onClick : function(e)
28450     {
28451         this.fireEvent("click", this, e);
28452     },
28453     
28454     onTriggerPress  : function(e)
28455     {   
28456         if (this.isVisible()) {
28457             this.hide();
28458         } else {
28459             this.show();
28460         }
28461     },
28462     
28463     isVisible : function(){
28464         return !this.hidden;
28465     },
28466     
28467     show : function()
28468     {
28469         this.fireEvent("beforeshow", this);
28470         
28471         this.hidden = false;
28472         this.el.addClass('open');
28473         
28474         Roo.get(document).on("mouseup", this.onMouseUp, this);
28475         
28476         this.fireEvent("show", this);
28477         
28478         
28479     },
28480     
28481     hide : function()
28482     {
28483         this.fireEvent("beforehide", this);
28484         
28485         this.hidden = true;
28486         this.el.removeClass('open');
28487         
28488         Roo.get(document).un("mouseup", this.onMouseUp);
28489         
28490         this.fireEvent("hide", this);
28491     },
28492     
28493     onMouseUp : function()
28494     {
28495         this.hide();
28496     }
28497     
28498 });
28499
28500  
28501  /*
28502  * - LGPL
28503  *
28504  * menu item
28505  * 
28506  */
28507 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28508
28509 /**
28510  * @class Roo.bootstrap.menu.Item
28511  * @extends Roo.bootstrap.Component
28512  * Bootstrap MenuItem class
28513  * @cfg {Boolean} submenu (true | false) default false
28514  * @cfg {String} html text of the item
28515  * @cfg {String} href the link
28516  * @cfg {Boolean} disable (true | false) default false
28517  * @cfg {Boolean} preventDefault (true | false) default true
28518  * @cfg {String} icon Font awesome icon
28519  * @cfg {String} pos Submenu align to (left | right) default right 
28520  * 
28521  * 
28522  * @constructor
28523  * Create a new Item
28524  * @param {Object} config The config object
28525  */
28526
28527
28528 Roo.bootstrap.menu.Item = function(config){
28529     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28530     this.addEvents({
28531         /**
28532          * @event mouseover
28533          * Fires when the mouse is hovering over this menu
28534          * @param {Roo.bootstrap.menu.Item} this
28535          * @param {Roo.EventObject} e
28536          */
28537         mouseover : true,
28538         /**
28539          * @event mouseout
28540          * Fires when the mouse exits this menu
28541          * @param {Roo.bootstrap.menu.Item} this
28542          * @param {Roo.EventObject} e
28543          */
28544         mouseout : true,
28545         // raw events
28546         /**
28547          * @event click
28548          * The raw click event for the entire grid.
28549          * @param {Roo.EventObject} e
28550          */
28551         click : true
28552     });
28553 };
28554
28555 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28556     
28557     submenu : false,
28558     href : '',
28559     html : '',
28560     preventDefault: true,
28561     disable : false,
28562     icon : false,
28563     pos : 'right',
28564     
28565     getAutoCreate : function()
28566     {
28567         var text = [
28568             {
28569                 tag : 'span',
28570                 cls : 'roo-menu-item-text',
28571                 html : this.html
28572             }
28573         ];
28574         
28575         if(this.icon){
28576             text.unshift({
28577                 tag : 'i',
28578                 cls : 'fa ' + this.icon
28579             })
28580         }
28581         
28582         var cfg = {
28583             tag : 'li',
28584             cn : [
28585                 {
28586                     tag : 'a',
28587                     href : this.href || '#',
28588                     cn : text
28589                 }
28590             ]
28591         };
28592         
28593         if(this.disable){
28594             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28595         }
28596         
28597         if(this.submenu){
28598             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28599             
28600             if(this.pos == 'left'){
28601                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28602             }
28603         }
28604         
28605         return cfg;
28606     },
28607     
28608     initEvents : function() 
28609     {
28610         this.el.on('mouseover', this.onMouseOver, this);
28611         this.el.on('mouseout', this.onMouseOut, this);
28612         
28613         this.el.select('a', true).first().on('click', this.onClick, this);
28614         
28615     },
28616     
28617     onClick : function(e)
28618     {
28619         if(this.preventDefault){
28620             e.preventDefault();
28621         }
28622         
28623         this.fireEvent("click", this, e);
28624     },
28625     
28626     onMouseOver : function(e)
28627     {
28628         if(this.submenu && this.pos == 'left'){
28629             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28630         }
28631         
28632         this.fireEvent("mouseover", this, e);
28633     },
28634     
28635     onMouseOut : function(e)
28636     {
28637         this.fireEvent("mouseout", this, e);
28638     }
28639 });
28640
28641  
28642
28643  /*
28644  * - LGPL
28645  *
28646  * menu separator
28647  * 
28648  */
28649 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28650
28651 /**
28652  * @class Roo.bootstrap.menu.Separator
28653  * @extends Roo.bootstrap.Component
28654  * Bootstrap Separator class
28655  * 
28656  * @constructor
28657  * Create a new Separator
28658  * @param {Object} config The config object
28659  */
28660
28661
28662 Roo.bootstrap.menu.Separator = function(config){
28663     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28664 };
28665
28666 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28667     
28668     getAutoCreate : function(){
28669         var cfg = {
28670             tag : 'li',
28671             cls: 'divider'
28672         };
28673         
28674         return cfg;
28675     }
28676    
28677 });
28678
28679  
28680
28681  /*
28682  * - LGPL
28683  *
28684  * Tooltip
28685  * 
28686  */
28687
28688 /**
28689  * @class Roo.bootstrap.Tooltip
28690  * Bootstrap Tooltip class
28691  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28692  * to determine which dom element triggers the tooltip.
28693  * 
28694  * It needs to add support for additional attributes like tooltip-position
28695  * 
28696  * @constructor
28697  * Create a new Toolti
28698  * @param {Object} config The config object
28699  */
28700
28701 Roo.bootstrap.Tooltip = function(config){
28702     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28703     
28704     this.alignment = Roo.bootstrap.Tooltip.alignment;
28705     
28706     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28707         this.alignment = config.alignment;
28708     }
28709     
28710 };
28711
28712 Roo.apply(Roo.bootstrap.Tooltip, {
28713     /**
28714      * @function init initialize tooltip monitoring.
28715      * @static
28716      */
28717     currentEl : false,
28718     currentTip : false,
28719     currentRegion : false,
28720     
28721     //  init : delay?
28722     
28723     init : function()
28724     {
28725         Roo.get(document).on('mouseover', this.enter ,this);
28726         Roo.get(document).on('mouseout', this.leave, this);
28727          
28728         
28729         this.currentTip = new Roo.bootstrap.Tooltip();
28730     },
28731     
28732     enter : function(ev)
28733     {
28734         var dom = ev.getTarget();
28735         
28736         //Roo.log(['enter',dom]);
28737         var el = Roo.fly(dom);
28738         if (this.currentEl) {
28739             //Roo.log(dom);
28740             //Roo.log(this.currentEl);
28741             //Roo.log(this.currentEl.contains(dom));
28742             if (this.currentEl == el) {
28743                 return;
28744             }
28745             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28746                 return;
28747             }
28748
28749         }
28750         
28751         if (this.currentTip.el) {
28752             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28753         }    
28754         //Roo.log(ev);
28755         
28756         if(!el || el.dom == document){
28757             return;
28758         }
28759         
28760         var bindEl = el;
28761         
28762         // you can not look for children, as if el is the body.. then everythign is the child..
28763         if (!el.attr('tooltip')) { //
28764             if (!el.select("[tooltip]").elements.length) {
28765                 return;
28766             }
28767             // is the mouse over this child...?
28768             bindEl = el.select("[tooltip]").first();
28769             var xy = ev.getXY();
28770             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28771                 //Roo.log("not in region.");
28772                 return;
28773             }
28774             //Roo.log("child element over..");
28775             
28776         }
28777         this.currentEl = bindEl;
28778         this.currentTip.bind(bindEl);
28779         this.currentRegion = Roo.lib.Region.getRegion(dom);
28780         this.currentTip.enter();
28781         
28782     },
28783     leave : function(ev)
28784     {
28785         var dom = ev.getTarget();
28786         //Roo.log(['leave',dom]);
28787         if (!this.currentEl) {
28788             return;
28789         }
28790         
28791         
28792         if (dom != this.currentEl.dom) {
28793             return;
28794         }
28795         var xy = ev.getXY();
28796         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28797             return;
28798         }
28799         // only activate leave if mouse cursor is outside... bounding box..
28800         
28801         
28802         
28803         
28804         if (this.currentTip) {
28805             this.currentTip.leave();
28806         }
28807         //Roo.log('clear currentEl');
28808         this.currentEl = false;
28809         
28810         
28811     },
28812     alignment : {
28813         'left' : ['r-l', [-2,0], 'right'],
28814         'right' : ['l-r', [2,0], 'left'],
28815         'bottom' : ['t-b', [0,2], 'top'],
28816         'top' : [ 'b-t', [0,-2], 'bottom']
28817     }
28818     
28819 });
28820
28821
28822 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28823     
28824     
28825     bindEl : false,
28826     
28827     delay : null, // can be { show : 300 , hide: 500}
28828     
28829     timeout : null,
28830     
28831     hoverState : null, //???
28832     
28833     placement : 'bottom', 
28834     
28835     alignment : false,
28836     
28837     getAutoCreate : function(){
28838     
28839         var cfg = {
28840            cls : 'tooltip',   
28841            role : 'tooltip',
28842            cn : [
28843                 {
28844                     cls : 'tooltip-arrow arrow'
28845                 },
28846                 {
28847                     cls : 'tooltip-inner'
28848                 }
28849            ]
28850         };
28851         
28852         return cfg;
28853     },
28854     bind : function(el)
28855     {
28856         this.bindEl = el;
28857     },
28858     
28859     initEvents : function()
28860     {
28861         this.arrowEl = this.el.select('.arrow', true).first();
28862         this.innerEl = this.el.select('.tooltip-inner', true).first();
28863     },
28864     
28865     enter : function () {
28866        
28867         if (this.timeout != null) {
28868             clearTimeout(this.timeout);
28869         }
28870         
28871         this.hoverState = 'in';
28872          //Roo.log("enter - show");
28873         if (!this.delay || !this.delay.show) {
28874             this.show();
28875             return;
28876         }
28877         var _t = this;
28878         this.timeout = setTimeout(function () {
28879             if (_t.hoverState == 'in') {
28880                 _t.show();
28881             }
28882         }, this.delay.show);
28883     },
28884     leave : function()
28885     {
28886         clearTimeout(this.timeout);
28887     
28888         this.hoverState = 'out';
28889          if (!this.delay || !this.delay.hide) {
28890             this.hide();
28891             return;
28892         }
28893        
28894         var _t = this;
28895         this.timeout = setTimeout(function () {
28896             //Roo.log("leave - timeout");
28897             
28898             if (_t.hoverState == 'out') {
28899                 _t.hide();
28900                 Roo.bootstrap.Tooltip.currentEl = false;
28901             }
28902         }, delay);
28903     },
28904     
28905     show : function (msg)
28906     {
28907         if (!this.el) {
28908             this.render(document.body);
28909         }
28910         // set content.
28911         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28912         
28913         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28914         
28915         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28916         
28917         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28918                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28919         
28920         var placement = typeof this.placement == 'function' ?
28921             this.placement.call(this, this.el, on_el) :
28922             this.placement;
28923             
28924         var autoToken = /\s?auto?\s?/i;
28925         var autoPlace = autoToken.test(placement);
28926         if (autoPlace) {
28927             placement = placement.replace(autoToken, '') || 'top';
28928         }
28929         
28930         //this.el.detach()
28931         //this.el.setXY([0,0]);
28932         this.el.show();
28933         //this.el.dom.style.display='block';
28934         
28935         //this.el.appendTo(on_el);
28936         
28937         var p = this.getPosition();
28938         var box = this.el.getBox();
28939         
28940         if (autoPlace) {
28941             // fixme..
28942         }
28943         
28944         var align = this.alignment[placement];
28945         
28946         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28947         
28948         if(placement == 'top' || placement == 'bottom'){
28949             if(xy[0] < 0){
28950                 placement = 'right';
28951             }
28952             
28953             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28954                 placement = 'left';
28955             }
28956             
28957             var scroll = Roo.select('body', true).first().getScroll();
28958             
28959             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28960                 placement = 'top';
28961             }
28962             
28963             align = this.alignment[placement];
28964             
28965             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28966             
28967         }
28968         
28969         this.el.alignTo(this.bindEl, align[0],align[1]);
28970         //var arrow = this.el.select('.arrow',true).first();
28971         //arrow.set(align[2], 
28972         
28973         this.el.addClass(placement);
28974         this.el.addClass("bs-tooltip-"+ placement);
28975         
28976         this.el.addClass('in fade show');
28977         
28978         this.hoverState = null;
28979         
28980         if (this.el.hasClass('fade')) {
28981             // fade it?
28982         }
28983         
28984         
28985         
28986         
28987         
28988     },
28989     hide : function()
28990     {
28991          
28992         if (!this.el) {
28993             return;
28994         }
28995         //this.el.setXY([0,0]);
28996         this.el.removeClass(['show', 'in']);
28997         //this.el.hide();
28998         
28999     }
29000     
29001 });
29002  
29003
29004  /*
29005  * - LGPL
29006  *
29007  * Location Picker
29008  * 
29009  */
29010
29011 /**
29012  * @class Roo.bootstrap.LocationPicker
29013  * @extends Roo.bootstrap.Component
29014  * Bootstrap LocationPicker class
29015  * @cfg {Number} latitude Position when init default 0
29016  * @cfg {Number} longitude Position when init default 0
29017  * @cfg {Number} zoom default 15
29018  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
29019  * @cfg {Boolean} mapTypeControl default false
29020  * @cfg {Boolean} disableDoubleClickZoom default false
29021  * @cfg {Boolean} scrollwheel default true
29022  * @cfg {Boolean} streetViewControl default false
29023  * @cfg {Number} radius default 0
29024  * @cfg {String} locationName
29025  * @cfg {Boolean} draggable default true
29026  * @cfg {Boolean} enableAutocomplete default false
29027  * @cfg {Boolean} enableReverseGeocode default true
29028  * @cfg {String} markerTitle
29029  * 
29030  * @constructor
29031  * Create a new LocationPicker
29032  * @param {Object} config The config object
29033  */
29034
29035
29036 Roo.bootstrap.LocationPicker = function(config){
29037     
29038     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
29039     
29040     this.addEvents({
29041         /**
29042          * @event initial
29043          * Fires when the picker initialized.
29044          * @param {Roo.bootstrap.LocationPicker} this
29045          * @param {Google Location} location
29046          */
29047         initial : true,
29048         /**
29049          * @event positionchanged
29050          * Fires when the picker position changed.
29051          * @param {Roo.bootstrap.LocationPicker} this
29052          * @param {Google Location} location
29053          */
29054         positionchanged : true,
29055         /**
29056          * @event resize
29057          * Fires when the map resize.
29058          * @param {Roo.bootstrap.LocationPicker} this
29059          */
29060         resize : true,
29061         /**
29062          * @event show
29063          * Fires when the map show.
29064          * @param {Roo.bootstrap.LocationPicker} this
29065          */
29066         show : true,
29067         /**
29068          * @event hide
29069          * Fires when the map hide.
29070          * @param {Roo.bootstrap.LocationPicker} this
29071          */
29072         hide : true,
29073         /**
29074          * @event mapClick
29075          * Fires when click the map.
29076          * @param {Roo.bootstrap.LocationPicker} this
29077          * @param {Map event} e
29078          */
29079         mapClick : true,
29080         /**
29081          * @event mapRightClick
29082          * Fires when right click the map.
29083          * @param {Roo.bootstrap.LocationPicker} this
29084          * @param {Map event} e
29085          */
29086         mapRightClick : true,
29087         /**
29088          * @event markerClick
29089          * Fires when click the marker.
29090          * @param {Roo.bootstrap.LocationPicker} this
29091          * @param {Map event} e
29092          */
29093         markerClick : true,
29094         /**
29095          * @event markerRightClick
29096          * Fires when right click the marker.
29097          * @param {Roo.bootstrap.LocationPicker} this
29098          * @param {Map event} e
29099          */
29100         markerRightClick : true,
29101         /**
29102          * @event OverlayViewDraw
29103          * Fires when OverlayView Draw
29104          * @param {Roo.bootstrap.LocationPicker} this
29105          */
29106         OverlayViewDraw : true,
29107         /**
29108          * @event OverlayViewOnAdd
29109          * Fires when OverlayView Draw
29110          * @param {Roo.bootstrap.LocationPicker} this
29111          */
29112         OverlayViewOnAdd : true,
29113         /**
29114          * @event OverlayViewOnRemove
29115          * Fires when OverlayView Draw
29116          * @param {Roo.bootstrap.LocationPicker} this
29117          */
29118         OverlayViewOnRemove : true,
29119         /**
29120          * @event OverlayViewShow
29121          * Fires when OverlayView Draw
29122          * @param {Roo.bootstrap.LocationPicker} this
29123          * @param {Pixel} cpx
29124          */
29125         OverlayViewShow : true,
29126         /**
29127          * @event OverlayViewHide
29128          * Fires when OverlayView Draw
29129          * @param {Roo.bootstrap.LocationPicker} this
29130          */
29131         OverlayViewHide : true,
29132         /**
29133          * @event loadexception
29134          * Fires when load google lib failed.
29135          * @param {Roo.bootstrap.LocationPicker} this
29136          */
29137         loadexception : true
29138     });
29139         
29140 };
29141
29142 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29143     
29144     gMapContext: false,
29145     
29146     latitude: 0,
29147     longitude: 0,
29148     zoom: 15,
29149     mapTypeId: false,
29150     mapTypeControl: false,
29151     disableDoubleClickZoom: false,
29152     scrollwheel: true,
29153     streetViewControl: false,
29154     radius: 0,
29155     locationName: '',
29156     draggable: true,
29157     enableAutocomplete: false,
29158     enableReverseGeocode: true,
29159     markerTitle: '',
29160     
29161     getAutoCreate: function()
29162     {
29163
29164         var cfg = {
29165             tag: 'div',
29166             cls: 'roo-location-picker'
29167         };
29168         
29169         return cfg
29170     },
29171     
29172     initEvents: function(ct, position)
29173     {       
29174         if(!this.el.getWidth() || this.isApplied()){
29175             return;
29176         }
29177         
29178         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29179         
29180         this.initial();
29181     },
29182     
29183     initial: function()
29184     {
29185         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29186             this.fireEvent('loadexception', this);
29187             return;
29188         }
29189         
29190         if(!this.mapTypeId){
29191             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29192         }
29193         
29194         this.gMapContext = this.GMapContext();
29195         
29196         this.initOverlayView();
29197         
29198         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29199         
29200         var _this = this;
29201                 
29202         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29203             _this.setPosition(_this.gMapContext.marker.position);
29204         });
29205         
29206         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29207             _this.fireEvent('mapClick', this, event);
29208             
29209         });
29210
29211         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29212             _this.fireEvent('mapRightClick', this, event);
29213             
29214         });
29215         
29216         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29217             _this.fireEvent('markerClick', this, event);
29218             
29219         });
29220
29221         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29222             _this.fireEvent('markerRightClick', this, event);
29223             
29224         });
29225         
29226         this.setPosition(this.gMapContext.location);
29227         
29228         this.fireEvent('initial', this, this.gMapContext.location);
29229     },
29230     
29231     initOverlayView: function()
29232     {
29233         var _this = this;
29234         
29235         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29236             
29237             draw: function()
29238             {
29239                 _this.fireEvent('OverlayViewDraw', _this);
29240             },
29241             
29242             onAdd: function()
29243             {
29244                 _this.fireEvent('OverlayViewOnAdd', _this);
29245             },
29246             
29247             onRemove: function()
29248             {
29249                 _this.fireEvent('OverlayViewOnRemove', _this);
29250             },
29251             
29252             show: function(cpx)
29253             {
29254                 _this.fireEvent('OverlayViewShow', _this, cpx);
29255             },
29256             
29257             hide: function()
29258             {
29259                 _this.fireEvent('OverlayViewHide', _this);
29260             }
29261             
29262         });
29263     },
29264     
29265     fromLatLngToContainerPixel: function(event)
29266     {
29267         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29268     },
29269     
29270     isApplied: function() 
29271     {
29272         return this.getGmapContext() == false ? false : true;
29273     },
29274     
29275     getGmapContext: function() 
29276     {
29277         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29278     },
29279     
29280     GMapContext: function() 
29281     {
29282         var position = new google.maps.LatLng(this.latitude, this.longitude);
29283         
29284         var _map = new google.maps.Map(this.el.dom, {
29285             center: position,
29286             zoom: this.zoom,
29287             mapTypeId: this.mapTypeId,
29288             mapTypeControl: this.mapTypeControl,
29289             disableDoubleClickZoom: this.disableDoubleClickZoom,
29290             scrollwheel: this.scrollwheel,
29291             streetViewControl: this.streetViewControl,
29292             locationName: this.locationName,
29293             draggable: this.draggable,
29294             enableAutocomplete: this.enableAutocomplete,
29295             enableReverseGeocode: this.enableReverseGeocode
29296         });
29297         
29298         var _marker = new google.maps.Marker({
29299             position: position,
29300             map: _map,
29301             title: this.markerTitle,
29302             draggable: this.draggable
29303         });
29304         
29305         return {
29306             map: _map,
29307             marker: _marker,
29308             circle: null,
29309             location: position,
29310             radius: this.radius,
29311             locationName: this.locationName,
29312             addressComponents: {
29313                 formatted_address: null,
29314                 addressLine1: null,
29315                 addressLine2: null,
29316                 streetName: null,
29317                 streetNumber: null,
29318                 city: null,
29319                 district: null,
29320                 state: null,
29321                 stateOrProvince: null
29322             },
29323             settings: this,
29324             domContainer: this.el.dom,
29325             geodecoder: new google.maps.Geocoder()
29326         };
29327     },
29328     
29329     drawCircle: function(center, radius, options) 
29330     {
29331         if (this.gMapContext.circle != null) {
29332             this.gMapContext.circle.setMap(null);
29333         }
29334         if (radius > 0) {
29335             radius *= 1;
29336             options = Roo.apply({}, options, {
29337                 strokeColor: "#0000FF",
29338                 strokeOpacity: .35,
29339                 strokeWeight: 2,
29340                 fillColor: "#0000FF",
29341                 fillOpacity: .2
29342             });
29343             
29344             options.map = this.gMapContext.map;
29345             options.radius = radius;
29346             options.center = center;
29347             this.gMapContext.circle = new google.maps.Circle(options);
29348             return this.gMapContext.circle;
29349         }
29350         
29351         return null;
29352     },
29353     
29354     setPosition: function(location) 
29355     {
29356         this.gMapContext.location = location;
29357         this.gMapContext.marker.setPosition(location);
29358         this.gMapContext.map.panTo(location);
29359         this.drawCircle(location, this.gMapContext.radius, {});
29360         
29361         var _this = this;
29362         
29363         if (this.gMapContext.settings.enableReverseGeocode) {
29364             this.gMapContext.geodecoder.geocode({
29365                 latLng: this.gMapContext.location
29366             }, function(results, status) {
29367                 
29368                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29369                     _this.gMapContext.locationName = results[0].formatted_address;
29370                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29371                     
29372                     _this.fireEvent('positionchanged', this, location);
29373                 }
29374             });
29375             
29376             return;
29377         }
29378         
29379         this.fireEvent('positionchanged', this, location);
29380     },
29381     
29382     resize: function()
29383     {
29384         google.maps.event.trigger(this.gMapContext.map, "resize");
29385         
29386         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29387         
29388         this.fireEvent('resize', this);
29389     },
29390     
29391     setPositionByLatLng: function(latitude, longitude)
29392     {
29393         this.setPosition(new google.maps.LatLng(latitude, longitude));
29394     },
29395     
29396     getCurrentPosition: function() 
29397     {
29398         return {
29399             latitude: this.gMapContext.location.lat(),
29400             longitude: this.gMapContext.location.lng()
29401         };
29402     },
29403     
29404     getAddressName: function() 
29405     {
29406         return this.gMapContext.locationName;
29407     },
29408     
29409     getAddressComponents: function() 
29410     {
29411         return this.gMapContext.addressComponents;
29412     },
29413     
29414     address_component_from_google_geocode: function(address_components) 
29415     {
29416         var result = {};
29417         
29418         for (var i = 0; i < address_components.length; i++) {
29419             var component = address_components[i];
29420             if (component.types.indexOf("postal_code") >= 0) {
29421                 result.postalCode = component.short_name;
29422             } else if (component.types.indexOf("street_number") >= 0) {
29423                 result.streetNumber = component.short_name;
29424             } else if (component.types.indexOf("route") >= 0) {
29425                 result.streetName = component.short_name;
29426             } else if (component.types.indexOf("neighborhood") >= 0) {
29427                 result.city = component.short_name;
29428             } else if (component.types.indexOf("locality") >= 0) {
29429                 result.city = component.short_name;
29430             } else if (component.types.indexOf("sublocality") >= 0) {
29431                 result.district = component.short_name;
29432             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29433                 result.stateOrProvince = component.short_name;
29434             } else if (component.types.indexOf("country") >= 0) {
29435                 result.country = component.short_name;
29436             }
29437         }
29438         
29439         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29440         result.addressLine2 = "";
29441         return result;
29442     },
29443     
29444     setZoomLevel: function(zoom)
29445     {
29446         this.gMapContext.map.setZoom(zoom);
29447     },
29448     
29449     show: function()
29450     {
29451         if(!this.el){
29452             return;
29453         }
29454         
29455         this.el.show();
29456         
29457         this.resize();
29458         
29459         this.fireEvent('show', this);
29460     },
29461     
29462     hide: function()
29463     {
29464         if(!this.el){
29465             return;
29466         }
29467         
29468         this.el.hide();
29469         
29470         this.fireEvent('hide', this);
29471     }
29472     
29473 });
29474
29475 Roo.apply(Roo.bootstrap.LocationPicker, {
29476     
29477     OverlayView : function(map, options)
29478     {
29479         options = options || {};
29480         
29481         this.setMap(map);
29482     }
29483     
29484     
29485 });/**
29486  * @class Roo.bootstrap.Alert
29487  * @extends Roo.bootstrap.Component
29488  * Bootstrap Alert class - shows an alert area box
29489  * eg
29490  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29491   Enter a valid email address
29492 </div>
29493  * @licence LGPL
29494  * @cfg {String} title The title of alert
29495  * @cfg {String} html The content of alert
29496  * @cfg {String} weight (  success | info | warning | danger )
29497  * @cfg {String} faicon font-awesomeicon
29498  * 
29499  * @constructor
29500  * Create a new alert
29501  * @param {Object} config The config object
29502  */
29503
29504
29505 Roo.bootstrap.Alert = function(config){
29506     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29507     
29508 };
29509
29510 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29511     
29512     title: '',
29513     html: '',
29514     weight: false,
29515     faicon: false,
29516     
29517     getAutoCreate : function()
29518     {
29519         
29520         var cfg = {
29521             tag : 'div',
29522             cls : 'alert',
29523             cn : [
29524                 {
29525                     tag : 'i',
29526                     cls : 'roo-alert-icon'
29527                     
29528                 },
29529                 {
29530                     tag : 'b',
29531                     cls : 'roo-alert-title',
29532                     html : this.title
29533                 },
29534                 {
29535                     tag : 'span',
29536                     cls : 'roo-alert-text',
29537                     html : this.html
29538                 }
29539             ]
29540         };
29541         
29542         if(this.faicon){
29543             cfg.cn[0].cls += ' fa ' + this.faicon;
29544         }
29545         
29546         if(this.weight){
29547             cfg.cls += ' alert-' + this.weight;
29548         }
29549         
29550         return cfg;
29551     },
29552     
29553     initEvents: function() 
29554     {
29555         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29556     },
29557     
29558     setTitle : function(str)
29559     {
29560         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29561     },
29562     
29563     setText : function(str)
29564     {
29565         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29566     },
29567     
29568     setWeight : function(weight)
29569     {
29570         if(this.weight){
29571             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29572         }
29573         
29574         this.weight = weight;
29575         
29576         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29577     },
29578     
29579     setIcon : function(icon)
29580     {
29581         if(this.faicon){
29582             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29583         }
29584         
29585         this.faicon = icon;
29586         
29587         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29588     },
29589     
29590     hide: function() 
29591     {
29592         this.el.hide();   
29593     },
29594     
29595     show: function() 
29596     {  
29597         this.el.show();   
29598     }
29599     
29600 });
29601
29602  
29603 /*
29604 * Licence: LGPL
29605 */
29606
29607 /**
29608  * @class Roo.bootstrap.UploadCropbox
29609  * @extends Roo.bootstrap.Component
29610  * Bootstrap UploadCropbox class
29611  * @cfg {String} emptyText show when image has been loaded
29612  * @cfg {String} rotateNotify show when image too small to rotate
29613  * @cfg {Number} errorTimeout default 3000
29614  * @cfg {Number} minWidth default 300
29615  * @cfg {Number} minHeight default 300
29616  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29617  * @cfg {Boolean} isDocument (true|false) default false
29618  * @cfg {String} url action url
29619  * @cfg {String} paramName default 'imageUpload'
29620  * @cfg {String} method default POST
29621  * @cfg {Boolean} loadMask (true|false) default true
29622  * @cfg {Boolean} loadingText default 'Loading...'
29623  * 
29624  * @constructor
29625  * Create a new UploadCropbox
29626  * @param {Object} config The config object
29627  */
29628
29629 Roo.bootstrap.UploadCropbox = function(config){
29630     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29631     
29632     this.addEvents({
29633         /**
29634          * @event beforeselectfile
29635          * Fire before select file
29636          * @param {Roo.bootstrap.UploadCropbox} this
29637          */
29638         "beforeselectfile" : true,
29639         /**
29640          * @event initial
29641          * Fire after initEvent
29642          * @param {Roo.bootstrap.UploadCropbox} this
29643          */
29644         "initial" : true,
29645         /**
29646          * @event crop
29647          * Fire after initEvent
29648          * @param {Roo.bootstrap.UploadCropbox} this
29649          * @param {String} data
29650          */
29651         "crop" : true,
29652         /**
29653          * @event prepare
29654          * Fire when preparing the file data
29655          * @param {Roo.bootstrap.UploadCropbox} this
29656          * @param {Object} file
29657          */
29658         "prepare" : true,
29659         /**
29660          * @event exception
29661          * Fire when get exception
29662          * @param {Roo.bootstrap.UploadCropbox} this
29663          * @param {XMLHttpRequest} xhr
29664          */
29665         "exception" : true,
29666         /**
29667          * @event beforeloadcanvas
29668          * Fire before load the canvas
29669          * @param {Roo.bootstrap.UploadCropbox} this
29670          * @param {String} src
29671          */
29672         "beforeloadcanvas" : true,
29673         /**
29674          * @event trash
29675          * Fire when trash image
29676          * @param {Roo.bootstrap.UploadCropbox} this
29677          */
29678         "trash" : true,
29679         /**
29680          * @event download
29681          * Fire when download the image
29682          * @param {Roo.bootstrap.UploadCropbox} this
29683          */
29684         "download" : true,
29685         /**
29686          * @event footerbuttonclick
29687          * Fire when footerbuttonclick
29688          * @param {Roo.bootstrap.UploadCropbox} this
29689          * @param {String} type
29690          */
29691         "footerbuttonclick" : true,
29692         /**
29693          * @event resize
29694          * Fire when resize
29695          * @param {Roo.bootstrap.UploadCropbox} this
29696          */
29697         "resize" : true,
29698         /**
29699          * @event rotate
29700          * Fire when rotate the image
29701          * @param {Roo.bootstrap.UploadCropbox} this
29702          * @param {String} pos
29703          */
29704         "rotate" : true,
29705         /**
29706          * @event inspect
29707          * Fire when inspect the file
29708          * @param {Roo.bootstrap.UploadCropbox} this
29709          * @param {Object} file
29710          */
29711         "inspect" : true,
29712         /**
29713          * @event upload
29714          * Fire when xhr upload the file
29715          * @param {Roo.bootstrap.UploadCropbox} this
29716          * @param {Object} data
29717          */
29718         "upload" : true,
29719         /**
29720          * @event arrange
29721          * Fire when arrange the file data
29722          * @param {Roo.bootstrap.UploadCropbox} this
29723          * @param {Object} formData
29724          */
29725         "arrange" : true
29726     });
29727     
29728     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29729 };
29730
29731 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29732     
29733     emptyText : 'Click to upload image',
29734     rotateNotify : 'Image is too small to rotate',
29735     errorTimeout : 3000,
29736     scale : 0,
29737     baseScale : 1,
29738     rotate : 0,
29739     dragable : false,
29740     pinching : false,
29741     mouseX : 0,
29742     mouseY : 0,
29743     cropData : false,
29744     minWidth : 300,
29745     minHeight : 300,
29746     file : false,
29747     exif : {},
29748     baseRotate : 1,
29749     cropType : 'image/jpeg',
29750     buttons : false,
29751     canvasLoaded : false,
29752     isDocument : false,
29753     method : 'POST',
29754     paramName : 'imageUpload',
29755     loadMask : true,
29756     loadingText : 'Loading...',
29757     maskEl : false,
29758     
29759     getAutoCreate : function()
29760     {
29761         var cfg = {
29762             tag : 'div',
29763             cls : 'roo-upload-cropbox',
29764             cn : [
29765                 {
29766                     tag : 'input',
29767                     cls : 'roo-upload-cropbox-selector',
29768                     type : 'file'
29769                 },
29770                 {
29771                     tag : 'div',
29772                     cls : 'roo-upload-cropbox-body',
29773                     style : 'cursor:pointer',
29774                     cn : [
29775                         {
29776                             tag : 'div',
29777                             cls : 'roo-upload-cropbox-preview'
29778                         },
29779                         {
29780                             tag : 'div',
29781                             cls : 'roo-upload-cropbox-thumb'
29782                         },
29783                         {
29784                             tag : 'div',
29785                             cls : 'roo-upload-cropbox-empty-notify',
29786                             html : this.emptyText
29787                         },
29788                         {
29789                             tag : 'div',
29790                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29791                             html : this.rotateNotify
29792                         }
29793                     ]
29794                 },
29795                 {
29796                     tag : 'div',
29797                     cls : 'roo-upload-cropbox-footer',
29798                     cn : {
29799                         tag : 'div',
29800                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29801                         cn : []
29802                     }
29803                 }
29804             ]
29805         };
29806         
29807         return cfg;
29808     },
29809     
29810     onRender : function(ct, position)
29811     {
29812         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29813         
29814         if (this.buttons.length) {
29815             
29816             Roo.each(this.buttons, function(bb) {
29817                 
29818                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29819                 
29820                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29821                 
29822             }, this);
29823         }
29824         
29825         if(this.loadMask){
29826             this.maskEl = this.el;
29827         }
29828     },
29829     
29830     initEvents : function()
29831     {
29832         this.urlAPI = (window.createObjectURL && window) || 
29833                                 (window.URL && URL.revokeObjectURL && URL) || 
29834                                 (window.webkitURL && webkitURL);
29835                         
29836         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29837         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29838         
29839         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29840         this.selectorEl.hide();
29841         
29842         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29843         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29844         
29845         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29846         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29847         this.thumbEl.hide();
29848         
29849         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29850         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29851         
29852         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29853         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29854         this.errorEl.hide();
29855         
29856         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29857         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29858         this.footerEl.hide();
29859         
29860         this.setThumbBoxSize();
29861         
29862         this.bind();
29863         
29864         this.resize();
29865         
29866         this.fireEvent('initial', this);
29867     },
29868
29869     bind : function()
29870     {
29871         var _this = this;
29872         
29873         window.addEventListener("resize", function() { _this.resize(); } );
29874         
29875         this.bodyEl.on('click', this.beforeSelectFile, this);
29876         
29877         if(Roo.isTouch){
29878             this.bodyEl.on('touchstart', this.onTouchStart, this);
29879             this.bodyEl.on('touchmove', this.onTouchMove, this);
29880             this.bodyEl.on('touchend', this.onTouchEnd, this);
29881         }
29882         
29883         if(!Roo.isTouch){
29884             this.bodyEl.on('mousedown', this.onMouseDown, this);
29885             this.bodyEl.on('mousemove', this.onMouseMove, this);
29886             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29887             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29888             Roo.get(document).on('mouseup', this.onMouseUp, this);
29889         }
29890         
29891         this.selectorEl.on('change', this.onFileSelected, this);
29892     },
29893     
29894     reset : function()
29895     {    
29896         this.scale = 0;
29897         this.baseScale = 1;
29898         this.rotate = 0;
29899         this.baseRotate = 1;
29900         this.dragable = false;
29901         this.pinching = false;
29902         this.mouseX = 0;
29903         this.mouseY = 0;
29904         this.cropData = false;
29905         this.notifyEl.dom.innerHTML = this.emptyText;
29906         
29907         this.selectorEl.dom.value = '';
29908         
29909     },
29910     
29911     resize : function()
29912     {
29913         if(this.fireEvent('resize', this) != false){
29914             this.setThumbBoxPosition();
29915             this.setCanvasPosition();
29916         }
29917     },
29918     
29919     onFooterButtonClick : function(e, el, o, type)
29920     {
29921         switch (type) {
29922             case 'rotate-left' :
29923                 this.onRotateLeft(e);
29924                 break;
29925             case 'rotate-right' :
29926                 this.onRotateRight(e);
29927                 break;
29928             case 'picture' :
29929                 this.beforeSelectFile(e);
29930                 break;
29931             case 'trash' :
29932                 this.trash(e);
29933                 break;
29934             case 'crop' :
29935                 this.crop(e);
29936                 break;
29937             case 'download' :
29938                 this.download(e);
29939                 break;
29940             default :
29941                 break;
29942         }
29943         
29944         this.fireEvent('footerbuttonclick', this, type);
29945     },
29946     
29947     beforeSelectFile : function(e)
29948     {
29949         e.preventDefault();
29950         
29951         if(this.fireEvent('beforeselectfile', this) != false){
29952             this.selectorEl.dom.click();
29953         }
29954     },
29955     
29956     onFileSelected : function(e)
29957     {
29958         e.preventDefault();
29959         
29960         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29961             return;
29962         }
29963         
29964         var file = this.selectorEl.dom.files[0];
29965         
29966         if(this.fireEvent('inspect', this, file) != false){
29967             this.prepare(file);
29968         }
29969         
29970     },
29971     
29972     trash : function(e)
29973     {
29974         this.fireEvent('trash', this);
29975     },
29976     
29977     download : function(e)
29978     {
29979         this.fireEvent('download', this);
29980     },
29981     
29982     loadCanvas : function(src)
29983     {   
29984         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29985             
29986             this.reset();
29987             
29988             this.imageEl = document.createElement('img');
29989             
29990             var _this = this;
29991             
29992             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29993             
29994             this.imageEl.src = src;
29995         }
29996     },
29997     
29998     onLoadCanvas : function()
29999     {   
30000         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
30001         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
30002         
30003         this.bodyEl.un('click', this.beforeSelectFile, this);
30004         
30005         this.notifyEl.hide();
30006         this.thumbEl.show();
30007         this.footerEl.show();
30008         
30009         this.baseRotateLevel();
30010         
30011         if(this.isDocument){
30012             this.setThumbBoxSize();
30013         }
30014         
30015         this.setThumbBoxPosition();
30016         
30017         this.baseScaleLevel();
30018         
30019         this.draw();
30020         
30021         this.resize();
30022         
30023         this.canvasLoaded = true;
30024         
30025         if(this.loadMask){
30026             this.maskEl.unmask();
30027         }
30028         
30029     },
30030     
30031     setCanvasPosition : function()
30032     {   
30033         if(!this.canvasEl){
30034             return;
30035         }
30036         
30037         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
30038         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
30039         
30040         this.previewEl.setLeft(pw);
30041         this.previewEl.setTop(ph);
30042         
30043     },
30044     
30045     onMouseDown : function(e)
30046     {   
30047         e.stopEvent();
30048         
30049         this.dragable = true;
30050         this.pinching = false;
30051         
30052         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
30053             this.dragable = false;
30054             return;
30055         }
30056         
30057         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30058         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30059         
30060     },
30061     
30062     onMouseMove : function(e)
30063     {   
30064         e.stopEvent();
30065         
30066         if(!this.canvasLoaded){
30067             return;
30068         }
30069         
30070         if (!this.dragable){
30071             return;
30072         }
30073         
30074         var minX = Math.ceil(this.thumbEl.getLeft(true));
30075         var minY = Math.ceil(this.thumbEl.getTop(true));
30076         
30077         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
30078         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
30079         
30080         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30081         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30082         
30083         x = x - this.mouseX;
30084         y = y - this.mouseY;
30085         
30086         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
30087         var bgY = Math.ceil(y + this.previewEl.getTop(true));
30088         
30089         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
30090         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
30091         
30092         this.previewEl.setLeft(bgX);
30093         this.previewEl.setTop(bgY);
30094         
30095         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
30096         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
30097     },
30098     
30099     onMouseUp : function(e)
30100     {   
30101         e.stopEvent();
30102         
30103         this.dragable = false;
30104     },
30105     
30106     onMouseWheel : function(e)
30107     {   
30108         e.stopEvent();
30109         
30110         this.startScale = this.scale;
30111         
30112         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
30113         
30114         if(!this.zoomable()){
30115             this.scale = this.startScale;
30116             return;
30117         }
30118         
30119         this.draw();
30120         
30121         return;
30122     },
30123     
30124     zoomable : function()
30125     {
30126         var minScale = this.thumbEl.getWidth() / this.minWidth;
30127         
30128         if(this.minWidth < this.minHeight){
30129             minScale = this.thumbEl.getHeight() / this.minHeight;
30130         }
30131         
30132         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
30133         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
30134         
30135         if(
30136                 this.isDocument &&
30137                 (this.rotate == 0 || this.rotate == 180) && 
30138                 (
30139                     width > this.imageEl.OriginWidth || 
30140                     height > this.imageEl.OriginHeight ||
30141                     (width < this.minWidth && height < this.minHeight)
30142                 )
30143         ){
30144             return false;
30145         }
30146         
30147         if(
30148                 this.isDocument &&
30149                 (this.rotate == 90 || this.rotate == 270) && 
30150                 (
30151                     width > this.imageEl.OriginWidth || 
30152                     height > this.imageEl.OriginHeight ||
30153                     (width < this.minHeight && height < this.minWidth)
30154                 )
30155         ){
30156             return false;
30157         }
30158         
30159         if(
30160                 !this.isDocument &&
30161                 (this.rotate == 0 || this.rotate == 180) && 
30162                 (
30163                     width < this.minWidth || 
30164                     width > this.imageEl.OriginWidth || 
30165                     height < this.minHeight || 
30166                     height > this.imageEl.OriginHeight
30167                 )
30168         ){
30169             return false;
30170         }
30171         
30172         if(
30173                 !this.isDocument &&
30174                 (this.rotate == 90 || this.rotate == 270) && 
30175                 (
30176                     width < this.minHeight || 
30177                     width > this.imageEl.OriginWidth || 
30178                     height < this.minWidth || 
30179                     height > this.imageEl.OriginHeight
30180                 )
30181         ){
30182             return false;
30183         }
30184         
30185         return true;
30186         
30187     },
30188     
30189     onRotateLeft : function(e)
30190     {   
30191         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30192             
30193             var minScale = this.thumbEl.getWidth() / this.minWidth;
30194             
30195             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30196             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30197             
30198             this.startScale = this.scale;
30199             
30200             while (this.getScaleLevel() < minScale){
30201             
30202                 this.scale = this.scale + 1;
30203                 
30204                 if(!this.zoomable()){
30205                     break;
30206                 }
30207                 
30208                 if(
30209                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30210                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30211                 ){
30212                     continue;
30213                 }
30214                 
30215                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30216
30217                 this.draw();
30218                 
30219                 return;
30220             }
30221             
30222             this.scale = this.startScale;
30223             
30224             this.onRotateFail();
30225             
30226             return false;
30227         }
30228         
30229         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30230
30231         if(this.isDocument){
30232             this.setThumbBoxSize();
30233             this.setThumbBoxPosition();
30234             this.setCanvasPosition();
30235         }
30236         
30237         this.draw();
30238         
30239         this.fireEvent('rotate', this, 'left');
30240         
30241     },
30242     
30243     onRotateRight : function(e)
30244     {
30245         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30246             
30247             var minScale = this.thumbEl.getWidth() / this.minWidth;
30248         
30249             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30250             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30251             
30252             this.startScale = this.scale;
30253             
30254             while (this.getScaleLevel() < minScale){
30255             
30256                 this.scale = this.scale + 1;
30257                 
30258                 if(!this.zoomable()){
30259                     break;
30260                 }
30261                 
30262                 if(
30263                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30264                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30265                 ){
30266                     continue;
30267                 }
30268                 
30269                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30270
30271                 this.draw();
30272                 
30273                 return;
30274             }
30275             
30276             this.scale = this.startScale;
30277             
30278             this.onRotateFail();
30279             
30280             return false;
30281         }
30282         
30283         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30284
30285         if(this.isDocument){
30286             this.setThumbBoxSize();
30287             this.setThumbBoxPosition();
30288             this.setCanvasPosition();
30289         }
30290         
30291         this.draw();
30292         
30293         this.fireEvent('rotate', this, 'right');
30294     },
30295     
30296     onRotateFail : function()
30297     {
30298         this.errorEl.show(true);
30299         
30300         var _this = this;
30301         
30302         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30303     },
30304     
30305     draw : function()
30306     {
30307         this.previewEl.dom.innerHTML = '';
30308         
30309         var canvasEl = document.createElement("canvas");
30310         
30311         var contextEl = canvasEl.getContext("2d");
30312         
30313         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30314         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30315         var center = this.imageEl.OriginWidth / 2;
30316         
30317         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30318             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30319             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30320             center = this.imageEl.OriginHeight / 2;
30321         }
30322         
30323         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30324         
30325         contextEl.translate(center, center);
30326         contextEl.rotate(this.rotate * Math.PI / 180);
30327
30328         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30329         
30330         this.canvasEl = document.createElement("canvas");
30331         
30332         this.contextEl = this.canvasEl.getContext("2d");
30333         
30334         switch (this.rotate) {
30335             case 0 :
30336                 
30337                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30338                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30339                 
30340                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30341                 
30342                 break;
30343             case 90 : 
30344                 
30345                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30346                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30347                 
30348                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30349                     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);
30350                     break;
30351                 }
30352                 
30353                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30354                 
30355                 break;
30356             case 180 :
30357                 
30358                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30359                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30360                 
30361                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30362                     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);
30363                     break;
30364                 }
30365                 
30366                 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);
30367                 
30368                 break;
30369             case 270 :
30370                 
30371                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30372                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30373         
30374                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30375                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30376                     break;
30377                 }
30378                 
30379                 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);
30380                 
30381                 break;
30382             default : 
30383                 break;
30384         }
30385         
30386         this.previewEl.appendChild(this.canvasEl);
30387         
30388         this.setCanvasPosition();
30389     },
30390     
30391     crop : function()
30392     {
30393         if(!this.canvasLoaded){
30394             return;
30395         }
30396         
30397         var imageCanvas = document.createElement("canvas");
30398         
30399         var imageContext = imageCanvas.getContext("2d");
30400         
30401         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30402         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30403         
30404         var center = imageCanvas.width / 2;
30405         
30406         imageContext.translate(center, center);
30407         
30408         imageContext.rotate(this.rotate * Math.PI / 180);
30409         
30410         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30411         
30412         var canvas = document.createElement("canvas");
30413         
30414         var context = canvas.getContext("2d");
30415                 
30416         canvas.width = this.minWidth;
30417         canvas.height = this.minHeight;
30418
30419         switch (this.rotate) {
30420             case 0 :
30421                 
30422                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30423                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30424                 
30425                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30426                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30427                 
30428                 var targetWidth = this.minWidth - 2 * x;
30429                 var targetHeight = this.minHeight - 2 * y;
30430                 
30431                 var scale = 1;
30432                 
30433                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30434                     scale = targetWidth / width;
30435                 }
30436                 
30437                 if(x > 0 && y == 0){
30438                     scale = targetHeight / height;
30439                 }
30440                 
30441                 if(x > 0 && y > 0){
30442                     scale = targetWidth / width;
30443                     
30444                     if(width < height){
30445                         scale = targetHeight / height;
30446                     }
30447                 }
30448                 
30449                 context.scale(scale, scale);
30450                 
30451                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30452                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30453
30454                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30455                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30456
30457                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30458                 
30459                 break;
30460             case 90 : 
30461                 
30462                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30463                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30464                 
30465                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30466                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30467                 
30468                 var targetWidth = this.minWidth - 2 * x;
30469                 var targetHeight = this.minHeight - 2 * y;
30470                 
30471                 var scale = 1;
30472                 
30473                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30474                     scale = targetWidth / width;
30475                 }
30476                 
30477                 if(x > 0 && y == 0){
30478                     scale = targetHeight / height;
30479                 }
30480                 
30481                 if(x > 0 && y > 0){
30482                     scale = targetWidth / width;
30483                     
30484                     if(width < height){
30485                         scale = targetHeight / height;
30486                     }
30487                 }
30488                 
30489                 context.scale(scale, scale);
30490                 
30491                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30492                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30493
30494                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30495                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30496                 
30497                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30498                 
30499                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30500                 
30501                 break;
30502             case 180 :
30503                 
30504                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30505                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30506                 
30507                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30508                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30509                 
30510                 var targetWidth = this.minWidth - 2 * x;
30511                 var targetHeight = this.minHeight - 2 * y;
30512                 
30513                 var scale = 1;
30514                 
30515                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30516                     scale = targetWidth / width;
30517                 }
30518                 
30519                 if(x > 0 && y == 0){
30520                     scale = targetHeight / height;
30521                 }
30522                 
30523                 if(x > 0 && y > 0){
30524                     scale = targetWidth / width;
30525                     
30526                     if(width < height){
30527                         scale = targetHeight / height;
30528                     }
30529                 }
30530                 
30531                 context.scale(scale, scale);
30532                 
30533                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30534                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30535
30536                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30537                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30538
30539                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30540                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30541                 
30542                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30543                 
30544                 break;
30545             case 270 :
30546                 
30547                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30548                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30549                 
30550                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30551                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30552                 
30553                 var targetWidth = this.minWidth - 2 * x;
30554                 var targetHeight = this.minHeight - 2 * y;
30555                 
30556                 var scale = 1;
30557                 
30558                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30559                     scale = targetWidth / width;
30560                 }
30561                 
30562                 if(x > 0 && y == 0){
30563                     scale = targetHeight / height;
30564                 }
30565                 
30566                 if(x > 0 && y > 0){
30567                     scale = targetWidth / width;
30568                     
30569                     if(width < height){
30570                         scale = targetHeight / height;
30571                     }
30572                 }
30573                 
30574                 context.scale(scale, scale);
30575                 
30576                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30577                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30578
30579                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30580                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30581                 
30582                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30583                 
30584                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30585                 
30586                 break;
30587             default : 
30588                 break;
30589         }
30590         
30591         this.cropData = canvas.toDataURL(this.cropType);
30592         
30593         if(this.fireEvent('crop', this, this.cropData) !== false){
30594             this.process(this.file, this.cropData);
30595         }
30596         
30597         return;
30598         
30599     },
30600     
30601     setThumbBoxSize : function()
30602     {
30603         var width, height;
30604         
30605         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30606             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30607             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30608             
30609             this.minWidth = width;
30610             this.minHeight = height;
30611             
30612             if(this.rotate == 90 || this.rotate == 270){
30613                 this.minWidth = height;
30614                 this.minHeight = width;
30615             }
30616         }
30617         
30618         height = 300;
30619         width = Math.ceil(this.minWidth * height / this.minHeight);
30620         
30621         if(this.minWidth > this.minHeight){
30622             width = 300;
30623             height = Math.ceil(this.minHeight * width / this.minWidth);
30624         }
30625         
30626         this.thumbEl.setStyle({
30627             width : width + 'px',
30628             height : height + 'px'
30629         });
30630
30631         return;
30632             
30633     },
30634     
30635     setThumbBoxPosition : function()
30636     {
30637         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30638         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30639         
30640         this.thumbEl.setLeft(x);
30641         this.thumbEl.setTop(y);
30642         
30643     },
30644     
30645     baseRotateLevel : function()
30646     {
30647         this.baseRotate = 1;
30648         
30649         if(
30650                 typeof(this.exif) != 'undefined' &&
30651                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30652                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30653         ){
30654             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30655         }
30656         
30657         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30658         
30659     },
30660     
30661     baseScaleLevel : function()
30662     {
30663         var width, height;
30664         
30665         if(this.isDocument){
30666             
30667             if(this.baseRotate == 6 || this.baseRotate == 8){
30668             
30669                 height = this.thumbEl.getHeight();
30670                 this.baseScale = height / this.imageEl.OriginWidth;
30671
30672                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30673                     width = this.thumbEl.getWidth();
30674                     this.baseScale = width / this.imageEl.OriginHeight;
30675                 }
30676
30677                 return;
30678             }
30679
30680             height = this.thumbEl.getHeight();
30681             this.baseScale = height / this.imageEl.OriginHeight;
30682
30683             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30684                 width = this.thumbEl.getWidth();
30685                 this.baseScale = width / this.imageEl.OriginWidth;
30686             }
30687
30688             return;
30689         }
30690         
30691         if(this.baseRotate == 6 || this.baseRotate == 8){
30692             
30693             width = this.thumbEl.getHeight();
30694             this.baseScale = width / this.imageEl.OriginHeight;
30695             
30696             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30697                 height = this.thumbEl.getWidth();
30698                 this.baseScale = height / this.imageEl.OriginHeight;
30699             }
30700             
30701             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30702                 height = this.thumbEl.getWidth();
30703                 this.baseScale = height / this.imageEl.OriginHeight;
30704                 
30705                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30706                     width = this.thumbEl.getHeight();
30707                     this.baseScale = width / this.imageEl.OriginWidth;
30708                 }
30709             }
30710             
30711             return;
30712         }
30713         
30714         width = this.thumbEl.getWidth();
30715         this.baseScale = width / this.imageEl.OriginWidth;
30716         
30717         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30718             height = this.thumbEl.getHeight();
30719             this.baseScale = height / this.imageEl.OriginHeight;
30720         }
30721         
30722         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30723             
30724             height = this.thumbEl.getHeight();
30725             this.baseScale = height / this.imageEl.OriginHeight;
30726             
30727             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30728                 width = this.thumbEl.getWidth();
30729                 this.baseScale = width / this.imageEl.OriginWidth;
30730             }
30731             
30732         }
30733         
30734         return;
30735     },
30736     
30737     getScaleLevel : function()
30738     {
30739         return this.baseScale * Math.pow(1.1, this.scale);
30740     },
30741     
30742     onTouchStart : function(e)
30743     {
30744         if(!this.canvasLoaded){
30745             this.beforeSelectFile(e);
30746             return;
30747         }
30748         
30749         var touches = e.browserEvent.touches;
30750         
30751         if(!touches){
30752             return;
30753         }
30754         
30755         if(touches.length == 1){
30756             this.onMouseDown(e);
30757             return;
30758         }
30759         
30760         if(touches.length != 2){
30761             return;
30762         }
30763         
30764         var coords = [];
30765         
30766         for(var i = 0, finger; finger = touches[i]; i++){
30767             coords.push(finger.pageX, finger.pageY);
30768         }
30769         
30770         var x = Math.pow(coords[0] - coords[2], 2);
30771         var y = Math.pow(coords[1] - coords[3], 2);
30772         
30773         this.startDistance = Math.sqrt(x + y);
30774         
30775         this.startScale = this.scale;
30776         
30777         this.pinching = true;
30778         this.dragable = false;
30779         
30780     },
30781     
30782     onTouchMove : function(e)
30783     {
30784         if(!this.pinching && !this.dragable){
30785             return;
30786         }
30787         
30788         var touches = e.browserEvent.touches;
30789         
30790         if(!touches){
30791             return;
30792         }
30793         
30794         if(this.dragable){
30795             this.onMouseMove(e);
30796             return;
30797         }
30798         
30799         var coords = [];
30800         
30801         for(var i = 0, finger; finger = touches[i]; i++){
30802             coords.push(finger.pageX, finger.pageY);
30803         }
30804         
30805         var x = Math.pow(coords[0] - coords[2], 2);
30806         var y = Math.pow(coords[1] - coords[3], 2);
30807         
30808         this.endDistance = Math.sqrt(x + y);
30809         
30810         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30811         
30812         if(!this.zoomable()){
30813             this.scale = this.startScale;
30814             return;
30815         }
30816         
30817         this.draw();
30818         
30819     },
30820     
30821     onTouchEnd : function(e)
30822     {
30823         this.pinching = false;
30824         this.dragable = false;
30825         
30826     },
30827     
30828     process : function(file, crop)
30829     {
30830         if(this.loadMask){
30831             this.maskEl.mask(this.loadingText);
30832         }
30833         
30834         this.xhr = new XMLHttpRequest();
30835         
30836         file.xhr = this.xhr;
30837
30838         this.xhr.open(this.method, this.url, true);
30839         
30840         var headers = {
30841             "Accept": "application/json",
30842             "Cache-Control": "no-cache",
30843             "X-Requested-With": "XMLHttpRequest"
30844         };
30845         
30846         for (var headerName in headers) {
30847             var headerValue = headers[headerName];
30848             if (headerValue) {
30849                 this.xhr.setRequestHeader(headerName, headerValue);
30850             }
30851         }
30852         
30853         var _this = this;
30854         
30855         this.xhr.onload = function()
30856         {
30857             _this.xhrOnLoad(_this.xhr);
30858         }
30859         
30860         this.xhr.onerror = function()
30861         {
30862             _this.xhrOnError(_this.xhr);
30863         }
30864         
30865         var formData = new FormData();
30866
30867         formData.append('returnHTML', 'NO');
30868         
30869         if(crop){
30870             formData.append('crop', crop);
30871         }
30872         
30873         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30874             formData.append(this.paramName, file, file.name);
30875         }
30876         
30877         if(typeof(file.filename) != 'undefined'){
30878             formData.append('filename', file.filename);
30879         }
30880         
30881         if(typeof(file.mimetype) != 'undefined'){
30882             formData.append('mimetype', file.mimetype);
30883         }
30884         
30885         if(this.fireEvent('arrange', this, formData) != false){
30886             this.xhr.send(formData);
30887         };
30888     },
30889     
30890     xhrOnLoad : function(xhr)
30891     {
30892         if(this.loadMask){
30893             this.maskEl.unmask();
30894         }
30895         
30896         if (xhr.readyState !== 4) {
30897             this.fireEvent('exception', this, xhr);
30898             return;
30899         }
30900
30901         var response = Roo.decode(xhr.responseText);
30902         
30903         if(!response.success){
30904             this.fireEvent('exception', this, xhr);
30905             return;
30906         }
30907         
30908         var response = Roo.decode(xhr.responseText);
30909         
30910         this.fireEvent('upload', this, response);
30911         
30912     },
30913     
30914     xhrOnError : function()
30915     {
30916         if(this.loadMask){
30917             this.maskEl.unmask();
30918         }
30919         
30920         Roo.log('xhr on error');
30921         
30922         var response = Roo.decode(xhr.responseText);
30923           
30924         Roo.log(response);
30925         
30926     },
30927     
30928     prepare : function(file)
30929     {   
30930         if(this.loadMask){
30931             this.maskEl.mask(this.loadingText);
30932         }
30933         
30934         this.file = false;
30935         this.exif = {};
30936         
30937         if(typeof(file) === 'string'){
30938             this.loadCanvas(file);
30939             return;
30940         }
30941         
30942         if(!file || !this.urlAPI){
30943             return;
30944         }
30945         
30946         this.file = file;
30947         this.cropType = file.type;
30948         
30949         var _this = this;
30950         
30951         if(this.fireEvent('prepare', this, this.file) != false){
30952             
30953             var reader = new FileReader();
30954             
30955             reader.onload = function (e) {
30956                 if (e.target.error) {
30957                     Roo.log(e.target.error);
30958                     return;
30959                 }
30960                 
30961                 var buffer = e.target.result,
30962                     dataView = new DataView(buffer),
30963                     offset = 2,
30964                     maxOffset = dataView.byteLength - 4,
30965                     markerBytes,
30966                     markerLength;
30967                 
30968                 if (dataView.getUint16(0) === 0xffd8) {
30969                     while (offset < maxOffset) {
30970                         markerBytes = dataView.getUint16(offset);
30971                         
30972                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30973                             markerLength = dataView.getUint16(offset + 2) + 2;
30974                             if (offset + markerLength > dataView.byteLength) {
30975                                 Roo.log('Invalid meta data: Invalid segment size.');
30976                                 break;
30977                             }
30978                             
30979                             if(markerBytes == 0xffe1){
30980                                 _this.parseExifData(
30981                                     dataView,
30982                                     offset,
30983                                     markerLength
30984                                 );
30985                             }
30986                             
30987                             offset += markerLength;
30988                             
30989                             continue;
30990                         }
30991                         
30992                         break;
30993                     }
30994                     
30995                 }
30996                 
30997                 var url = _this.urlAPI.createObjectURL(_this.file);
30998                 
30999                 _this.loadCanvas(url);
31000                 
31001                 return;
31002             }
31003             
31004             reader.readAsArrayBuffer(this.file);
31005             
31006         }
31007         
31008     },
31009     
31010     parseExifData : function(dataView, offset, length)
31011     {
31012         var tiffOffset = offset + 10,
31013             littleEndian,
31014             dirOffset;
31015     
31016         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31017             // No Exif data, might be XMP data instead
31018             return;
31019         }
31020         
31021         // Check for the ASCII code for "Exif" (0x45786966):
31022         if (dataView.getUint32(offset + 4) !== 0x45786966) {
31023             // No Exif data, might be XMP data instead
31024             return;
31025         }
31026         if (tiffOffset + 8 > dataView.byteLength) {
31027             Roo.log('Invalid Exif data: Invalid segment size.');
31028             return;
31029         }
31030         // Check for the two null bytes:
31031         if (dataView.getUint16(offset + 8) !== 0x0000) {
31032             Roo.log('Invalid Exif data: Missing byte alignment offset.');
31033             return;
31034         }
31035         // Check the byte alignment:
31036         switch (dataView.getUint16(tiffOffset)) {
31037         case 0x4949:
31038             littleEndian = true;
31039             break;
31040         case 0x4D4D:
31041             littleEndian = false;
31042             break;
31043         default:
31044             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
31045             return;
31046         }
31047         // Check for the TIFF tag marker (0x002A):
31048         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
31049             Roo.log('Invalid Exif data: Missing TIFF marker.');
31050             return;
31051         }
31052         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
31053         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
31054         
31055         this.parseExifTags(
31056             dataView,
31057             tiffOffset,
31058             tiffOffset + dirOffset,
31059             littleEndian
31060         );
31061     },
31062     
31063     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
31064     {
31065         var tagsNumber,
31066             dirEndOffset,
31067             i;
31068         if (dirOffset + 6 > dataView.byteLength) {
31069             Roo.log('Invalid Exif data: Invalid directory offset.');
31070             return;
31071         }
31072         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
31073         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
31074         if (dirEndOffset + 4 > dataView.byteLength) {
31075             Roo.log('Invalid Exif data: Invalid directory size.');
31076             return;
31077         }
31078         for (i = 0; i < tagsNumber; i += 1) {
31079             this.parseExifTag(
31080                 dataView,
31081                 tiffOffset,
31082                 dirOffset + 2 + 12 * i, // tag offset
31083                 littleEndian
31084             );
31085         }
31086         // Return the offset to the next directory:
31087         return dataView.getUint32(dirEndOffset, littleEndian);
31088     },
31089     
31090     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
31091     {
31092         var tag = dataView.getUint16(offset, littleEndian);
31093         
31094         this.exif[tag] = this.getExifValue(
31095             dataView,
31096             tiffOffset,
31097             offset,
31098             dataView.getUint16(offset + 2, littleEndian), // tag type
31099             dataView.getUint32(offset + 4, littleEndian), // tag length
31100             littleEndian
31101         );
31102     },
31103     
31104     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
31105     {
31106         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
31107             tagSize,
31108             dataOffset,
31109             values,
31110             i,
31111             str,
31112             c;
31113     
31114         if (!tagType) {
31115             Roo.log('Invalid Exif data: Invalid tag type.');
31116             return;
31117         }
31118         
31119         tagSize = tagType.size * length;
31120         // Determine if the value is contained in the dataOffset bytes,
31121         // or if the value at the dataOffset is a pointer to the actual data:
31122         dataOffset = tagSize > 4 ?
31123                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
31124         if (dataOffset + tagSize > dataView.byteLength) {
31125             Roo.log('Invalid Exif data: Invalid data offset.');
31126             return;
31127         }
31128         if (length === 1) {
31129             return tagType.getValue(dataView, dataOffset, littleEndian);
31130         }
31131         values = [];
31132         for (i = 0; i < length; i += 1) {
31133             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
31134         }
31135         
31136         if (tagType.ascii) {
31137             str = '';
31138             // Concatenate the chars:
31139             for (i = 0; i < values.length; i += 1) {
31140                 c = values[i];
31141                 // Ignore the terminating NULL byte(s):
31142                 if (c === '\u0000') {
31143                     break;
31144                 }
31145                 str += c;
31146             }
31147             return str;
31148         }
31149         return values;
31150     }
31151     
31152 });
31153
31154 Roo.apply(Roo.bootstrap.UploadCropbox, {
31155     tags : {
31156         'Orientation': 0x0112
31157     },
31158     
31159     Orientation: {
31160             1: 0, //'top-left',
31161 //            2: 'top-right',
31162             3: 180, //'bottom-right',
31163 //            4: 'bottom-left',
31164 //            5: 'left-top',
31165             6: 90, //'right-top',
31166 //            7: 'right-bottom',
31167             8: 270 //'left-bottom'
31168     },
31169     
31170     exifTagTypes : {
31171         // byte, 8-bit unsigned int:
31172         1: {
31173             getValue: function (dataView, dataOffset) {
31174                 return dataView.getUint8(dataOffset);
31175             },
31176             size: 1
31177         },
31178         // ascii, 8-bit byte:
31179         2: {
31180             getValue: function (dataView, dataOffset) {
31181                 return String.fromCharCode(dataView.getUint8(dataOffset));
31182             },
31183             size: 1,
31184             ascii: true
31185         },
31186         // short, 16 bit int:
31187         3: {
31188             getValue: function (dataView, dataOffset, littleEndian) {
31189                 return dataView.getUint16(dataOffset, littleEndian);
31190             },
31191             size: 2
31192         },
31193         // long, 32 bit int:
31194         4: {
31195             getValue: function (dataView, dataOffset, littleEndian) {
31196                 return dataView.getUint32(dataOffset, littleEndian);
31197             },
31198             size: 4
31199         },
31200         // rational = two long values, first is numerator, second is denominator:
31201         5: {
31202             getValue: function (dataView, dataOffset, littleEndian) {
31203                 return dataView.getUint32(dataOffset, littleEndian) /
31204                     dataView.getUint32(dataOffset + 4, littleEndian);
31205             },
31206             size: 8
31207         },
31208         // slong, 32 bit signed int:
31209         9: {
31210             getValue: function (dataView, dataOffset, littleEndian) {
31211                 return dataView.getInt32(dataOffset, littleEndian);
31212             },
31213             size: 4
31214         },
31215         // srational, two slongs, first is numerator, second is denominator:
31216         10: {
31217             getValue: function (dataView, dataOffset, littleEndian) {
31218                 return dataView.getInt32(dataOffset, littleEndian) /
31219                     dataView.getInt32(dataOffset + 4, littleEndian);
31220             },
31221             size: 8
31222         }
31223     },
31224     
31225     footer : {
31226         STANDARD : [
31227             {
31228                 tag : 'div',
31229                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31230                 action : 'rotate-left',
31231                 cn : [
31232                     {
31233                         tag : 'button',
31234                         cls : 'btn btn-default',
31235                         html : '<i class="fa fa-undo"></i>'
31236                     }
31237                 ]
31238             },
31239             {
31240                 tag : 'div',
31241                 cls : 'btn-group roo-upload-cropbox-picture',
31242                 action : 'picture',
31243                 cn : [
31244                     {
31245                         tag : 'button',
31246                         cls : 'btn btn-default',
31247                         html : '<i class="fa fa-picture-o"></i>'
31248                     }
31249                 ]
31250             },
31251             {
31252                 tag : 'div',
31253                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31254                 action : 'rotate-right',
31255                 cn : [
31256                     {
31257                         tag : 'button',
31258                         cls : 'btn btn-default',
31259                         html : '<i class="fa fa-repeat"></i>'
31260                     }
31261                 ]
31262             }
31263         ],
31264         DOCUMENT : [
31265             {
31266                 tag : 'div',
31267                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31268                 action : 'rotate-left',
31269                 cn : [
31270                     {
31271                         tag : 'button',
31272                         cls : 'btn btn-default',
31273                         html : '<i class="fa fa-undo"></i>'
31274                     }
31275                 ]
31276             },
31277             {
31278                 tag : 'div',
31279                 cls : 'btn-group roo-upload-cropbox-download',
31280                 action : 'download',
31281                 cn : [
31282                     {
31283                         tag : 'button',
31284                         cls : 'btn btn-default',
31285                         html : '<i class="fa fa-download"></i>'
31286                     }
31287                 ]
31288             },
31289             {
31290                 tag : 'div',
31291                 cls : 'btn-group roo-upload-cropbox-crop',
31292                 action : 'crop',
31293                 cn : [
31294                     {
31295                         tag : 'button',
31296                         cls : 'btn btn-default',
31297                         html : '<i class="fa fa-crop"></i>'
31298                     }
31299                 ]
31300             },
31301             {
31302                 tag : 'div',
31303                 cls : 'btn-group roo-upload-cropbox-trash',
31304                 action : 'trash',
31305                 cn : [
31306                     {
31307                         tag : 'button',
31308                         cls : 'btn btn-default',
31309                         html : '<i class="fa fa-trash"></i>'
31310                     }
31311                 ]
31312             },
31313             {
31314                 tag : 'div',
31315                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31316                 action : 'rotate-right',
31317                 cn : [
31318                     {
31319                         tag : 'button',
31320                         cls : 'btn btn-default',
31321                         html : '<i class="fa fa-repeat"></i>'
31322                     }
31323                 ]
31324             }
31325         ],
31326         ROTATOR : [
31327             {
31328                 tag : 'div',
31329                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31330                 action : 'rotate-left',
31331                 cn : [
31332                     {
31333                         tag : 'button',
31334                         cls : 'btn btn-default',
31335                         html : '<i class="fa fa-undo"></i>'
31336                     }
31337                 ]
31338             },
31339             {
31340                 tag : 'div',
31341                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31342                 action : 'rotate-right',
31343                 cn : [
31344                     {
31345                         tag : 'button',
31346                         cls : 'btn btn-default',
31347                         html : '<i class="fa fa-repeat"></i>'
31348                     }
31349                 ]
31350             }
31351         ]
31352     }
31353 });
31354
31355 /*
31356 * Licence: LGPL
31357 */
31358
31359 /**
31360  * @class Roo.bootstrap.DocumentManager
31361  * @extends Roo.bootstrap.Component
31362  * Bootstrap DocumentManager class
31363  * @cfg {String} paramName default 'imageUpload'
31364  * @cfg {String} toolTipName default 'filename'
31365  * @cfg {String} method default POST
31366  * @cfg {String} url action url
31367  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31368  * @cfg {Boolean} multiple multiple upload default true
31369  * @cfg {Number} thumbSize default 300
31370  * @cfg {String} fieldLabel
31371  * @cfg {Number} labelWidth default 4
31372  * @cfg {String} labelAlign (left|top) default left
31373  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31374 * @cfg {Number} labellg set the width of label (1-12)
31375  * @cfg {Number} labelmd set the width of label (1-12)
31376  * @cfg {Number} labelsm set the width of label (1-12)
31377  * @cfg {Number} labelxs set the width of label (1-12)
31378  * 
31379  * @constructor
31380  * Create a new DocumentManager
31381  * @param {Object} config The config object
31382  */
31383
31384 Roo.bootstrap.DocumentManager = function(config){
31385     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31386     
31387     this.files = [];
31388     this.delegates = [];
31389     
31390     this.addEvents({
31391         /**
31392          * @event initial
31393          * Fire when initial the DocumentManager
31394          * @param {Roo.bootstrap.DocumentManager} this
31395          */
31396         "initial" : true,
31397         /**
31398          * @event inspect
31399          * inspect selected file
31400          * @param {Roo.bootstrap.DocumentManager} this
31401          * @param {File} file
31402          */
31403         "inspect" : true,
31404         /**
31405          * @event exception
31406          * Fire when xhr load exception
31407          * @param {Roo.bootstrap.DocumentManager} this
31408          * @param {XMLHttpRequest} xhr
31409          */
31410         "exception" : true,
31411         /**
31412          * @event afterupload
31413          * Fire when xhr load exception
31414          * @param {Roo.bootstrap.DocumentManager} this
31415          * @param {XMLHttpRequest} xhr
31416          */
31417         "afterupload" : true,
31418         /**
31419          * @event prepare
31420          * prepare the form data
31421          * @param {Roo.bootstrap.DocumentManager} this
31422          * @param {Object} formData
31423          */
31424         "prepare" : true,
31425         /**
31426          * @event remove
31427          * Fire when remove the file
31428          * @param {Roo.bootstrap.DocumentManager} this
31429          * @param {Object} file
31430          */
31431         "remove" : true,
31432         /**
31433          * @event refresh
31434          * Fire after refresh the file
31435          * @param {Roo.bootstrap.DocumentManager} this
31436          */
31437         "refresh" : true,
31438         /**
31439          * @event click
31440          * Fire after click the image
31441          * @param {Roo.bootstrap.DocumentManager} this
31442          * @param {Object} file
31443          */
31444         "click" : true,
31445         /**
31446          * @event edit
31447          * Fire when upload a image and editable set to true
31448          * @param {Roo.bootstrap.DocumentManager} this
31449          * @param {Object} file
31450          */
31451         "edit" : true,
31452         /**
31453          * @event beforeselectfile
31454          * Fire before select file
31455          * @param {Roo.bootstrap.DocumentManager} this
31456          */
31457         "beforeselectfile" : true,
31458         /**
31459          * @event process
31460          * Fire before process file
31461          * @param {Roo.bootstrap.DocumentManager} this
31462          * @param {Object} file
31463          */
31464         "process" : true,
31465         /**
31466          * @event previewrendered
31467          * Fire when preview rendered
31468          * @param {Roo.bootstrap.DocumentManager} this
31469          * @param {Object} file
31470          */
31471         "previewrendered" : true,
31472         /**
31473          */
31474         "previewResize" : true
31475         
31476     });
31477 };
31478
31479 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31480     
31481     boxes : 0,
31482     inputName : '',
31483     thumbSize : 300,
31484     multiple : true,
31485     files : false,
31486     method : 'POST',
31487     url : '',
31488     paramName : 'imageUpload',
31489     toolTipName : 'filename',
31490     fieldLabel : '',
31491     labelWidth : 4,
31492     labelAlign : 'left',
31493     editable : true,
31494     delegates : false,
31495     xhr : false, 
31496     
31497     labellg : 0,
31498     labelmd : 0,
31499     labelsm : 0,
31500     labelxs : 0,
31501     
31502     getAutoCreate : function()
31503     {   
31504         var managerWidget = {
31505             tag : 'div',
31506             cls : 'roo-document-manager',
31507             cn : [
31508                 {
31509                     tag : 'input',
31510                     cls : 'roo-document-manager-selector',
31511                     type : 'file'
31512                 },
31513                 {
31514                     tag : 'div',
31515                     cls : 'roo-document-manager-uploader',
31516                     cn : [
31517                         {
31518                             tag : 'div',
31519                             cls : 'roo-document-manager-upload-btn',
31520                             html : '<i class="fa fa-plus"></i>'
31521                         }
31522                     ]
31523                     
31524                 }
31525             ]
31526         };
31527         
31528         var content = [
31529             {
31530                 tag : 'div',
31531                 cls : 'column col-md-12',
31532                 cn : managerWidget
31533             }
31534         ];
31535         
31536         if(this.fieldLabel.length){
31537             
31538             content = [
31539                 {
31540                     tag : 'div',
31541                     cls : 'column col-md-12',
31542                     html : this.fieldLabel
31543                 },
31544                 {
31545                     tag : 'div',
31546                     cls : 'column col-md-12',
31547                     cn : managerWidget
31548                 }
31549             ];
31550
31551             if(this.labelAlign == 'left'){
31552                 content = [
31553                     {
31554                         tag : 'div',
31555                         cls : 'column',
31556                         html : this.fieldLabel
31557                     },
31558                     {
31559                         tag : 'div',
31560                         cls : 'column',
31561                         cn : managerWidget
31562                     }
31563                 ];
31564                 
31565                 if(this.labelWidth > 12){
31566                     content[0].style = "width: " + this.labelWidth + 'px';
31567                 }
31568
31569                 if(this.labelWidth < 13 && this.labelmd == 0){
31570                     this.labelmd = this.labelWidth;
31571                 }
31572
31573                 if(this.labellg > 0){
31574                     content[0].cls += ' col-lg-' + this.labellg;
31575                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31576                 }
31577
31578                 if(this.labelmd > 0){
31579                     content[0].cls += ' col-md-' + this.labelmd;
31580                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31581                 }
31582
31583                 if(this.labelsm > 0){
31584                     content[0].cls += ' col-sm-' + this.labelsm;
31585                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31586                 }
31587
31588                 if(this.labelxs > 0){
31589                     content[0].cls += ' col-xs-' + this.labelxs;
31590                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31591                 }
31592                 
31593             }
31594         }
31595         
31596         var cfg = {
31597             tag : 'div',
31598             cls : 'row clearfix',
31599             cn : content
31600         };
31601         
31602         return cfg;
31603         
31604     },
31605     
31606     initEvents : function()
31607     {
31608         this.managerEl = this.el.select('.roo-document-manager', true).first();
31609         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31610         
31611         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31612         this.selectorEl.hide();
31613         
31614         if(this.multiple){
31615             this.selectorEl.attr('multiple', 'multiple');
31616         }
31617         
31618         this.selectorEl.on('change', this.onFileSelected, this);
31619         
31620         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31621         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31622         
31623         this.uploader.on('click', this.onUploaderClick, this);
31624         
31625         this.renderProgressDialog();
31626         
31627         var _this = this;
31628         
31629         window.addEventListener("resize", function() { _this.refresh(); } );
31630         
31631         this.fireEvent('initial', this);
31632     },
31633     
31634     renderProgressDialog : function()
31635     {
31636         var _this = this;
31637         
31638         this.progressDialog = new Roo.bootstrap.Modal({
31639             cls : 'roo-document-manager-progress-dialog',
31640             allow_close : false,
31641             animate : false,
31642             title : '',
31643             buttons : [
31644                 {
31645                     name  :'cancel',
31646                     weight : 'danger',
31647                     html : 'Cancel'
31648                 }
31649             ], 
31650             listeners : { 
31651                 btnclick : function() {
31652                     _this.uploadCancel();
31653                     this.hide();
31654                 }
31655             }
31656         });
31657          
31658         this.progressDialog.render(Roo.get(document.body));
31659          
31660         this.progress = new Roo.bootstrap.Progress({
31661             cls : 'roo-document-manager-progress',
31662             active : true,
31663             striped : true
31664         });
31665         
31666         this.progress.render(this.progressDialog.getChildContainer());
31667         
31668         this.progressBar = new Roo.bootstrap.ProgressBar({
31669             cls : 'roo-document-manager-progress-bar',
31670             aria_valuenow : 0,
31671             aria_valuemin : 0,
31672             aria_valuemax : 12,
31673             panel : 'success'
31674         });
31675         
31676         this.progressBar.render(this.progress.getChildContainer());
31677     },
31678     
31679     onUploaderClick : function(e)
31680     {
31681         e.preventDefault();
31682      
31683         if(this.fireEvent('beforeselectfile', this) != false){
31684             this.selectorEl.dom.click();
31685         }
31686         
31687     },
31688     
31689     onFileSelected : function(e)
31690     {
31691         e.preventDefault();
31692         
31693         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31694             return;
31695         }
31696         
31697         Roo.each(this.selectorEl.dom.files, function(file){
31698             if(this.fireEvent('inspect', this, file) != false){
31699                 this.files.push(file);
31700             }
31701         }, this);
31702         
31703         this.queue();
31704         
31705     },
31706     
31707     queue : function()
31708     {
31709         this.selectorEl.dom.value = '';
31710         
31711         if(!this.files || !this.files.length){
31712             return;
31713         }
31714         
31715         if(this.boxes > 0 && this.files.length > this.boxes){
31716             this.files = this.files.slice(0, this.boxes);
31717         }
31718         
31719         this.uploader.show();
31720         
31721         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31722             this.uploader.hide();
31723         }
31724         
31725         var _this = this;
31726         
31727         var files = [];
31728         
31729         var docs = [];
31730         
31731         Roo.each(this.files, function(file){
31732             
31733             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31734                 var f = this.renderPreview(file);
31735                 files.push(f);
31736                 return;
31737             }
31738             
31739             if(file.type.indexOf('image') != -1){
31740                 this.delegates.push(
31741                     (function(){
31742                         _this.process(file);
31743                     }).createDelegate(this)
31744                 );
31745         
31746                 return;
31747             }
31748             
31749             docs.push(
31750                 (function(){
31751                     _this.process(file);
31752                 }).createDelegate(this)
31753             );
31754             
31755         }, this);
31756         
31757         this.files = files;
31758         
31759         this.delegates = this.delegates.concat(docs);
31760         
31761         if(!this.delegates.length){
31762             this.refresh();
31763             return;
31764         }
31765         
31766         this.progressBar.aria_valuemax = this.delegates.length;
31767         
31768         this.arrange();
31769         
31770         return;
31771     },
31772     
31773     arrange : function()
31774     {
31775         if(!this.delegates.length){
31776             this.progressDialog.hide();
31777             this.refresh();
31778             return;
31779         }
31780         
31781         var delegate = this.delegates.shift();
31782         
31783         this.progressDialog.show();
31784         
31785         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31786         
31787         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31788         
31789         delegate();
31790     },
31791     
31792     refresh : function()
31793     {
31794         this.uploader.show();
31795         
31796         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31797             this.uploader.hide();
31798         }
31799         
31800         Roo.isTouch ? this.closable(false) : this.closable(true);
31801         
31802         this.fireEvent('refresh', this);
31803     },
31804     
31805     onRemove : function(e, el, o)
31806     {
31807         e.preventDefault();
31808         
31809         this.fireEvent('remove', this, o);
31810         
31811     },
31812     
31813     remove : function(o)
31814     {
31815         var files = [];
31816         
31817         Roo.each(this.files, function(file){
31818             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31819                 files.push(file);
31820                 return;
31821             }
31822
31823             o.target.remove();
31824
31825         }, this);
31826         
31827         this.files = files;
31828         
31829         this.refresh();
31830     },
31831     
31832     clear : function()
31833     {
31834         Roo.each(this.files, function(file){
31835             if(!file.target){
31836                 return;
31837             }
31838             
31839             file.target.remove();
31840
31841         }, this);
31842         
31843         this.files = [];
31844         
31845         this.refresh();
31846     },
31847     
31848     onClick : function(e, el, o)
31849     {
31850         e.preventDefault();
31851         
31852         this.fireEvent('click', this, o);
31853         
31854     },
31855     
31856     closable : function(closable)
31857     {
31858         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31859             
31860             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31861             
31862             if(closable){
31863                 el.show();
31864                 return;
31865             }
31866             
31867             el.hide();
31868             
31869         }, this);
31870     },
31871     
31872     xhrOnLoad : function(xhr)
31873     {
31874         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31875             el.remove();
31876         }, this);
31877         
31878         if (xhr.readyState !== 4) {
31879             this.arrange();
31880             this.fireEvent('exception', this, xhr);
31881             return;
31882         }
31883
31884         var response = Roo.decode(xhr.responseText);
31885         
31886         if(!response.success){
31887             this.arrange();
31888             this.fireEvent('exception', this, xhr);
31889             return;
31890         }
31891         
31892         var file = this.renderPreview(response.data);
31893         
31894         this.files.push(file);
31895         
31896         this.arrange();
31897         
31898         this.fireEvent('afterupload', this, xhr);
31899         
31900     },
31901     
31902     xhrOnError : function(xhr)
31903     {
31904         Roo.log('xhr on error');
31905         
31906         var response = Roo.decode(xhr.responseText);
31907           
31908         Roo.log(response);
31909         
31910         this.arrange();
31911     },
31912     
31913     process : function(file)
31914     {
31915         if(this.fireEvent('process', this, file) !== false){
31916             if(this.editable && file.type.indexOf('image') != -1){
31917                 this.fireEvent('edit', this, file);
31918                 return;
31919             }
31920
31921             this.uploadStart(file, false);
31922
31923             return;
31924         }
31925         
31926     },
31927     
31928     uploadStart : function(file, crop)
31929     {
31930         this.xhr = new XMLHttpRequest();
31931         
31932         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31933             this.arrange();
31934             return;
31935         }
31936         
31937         file.xhr = this.xhr;
31938             
31939         this.managerEl.createChild({
31940             tag : 'div',
31941             cls : 'roo-document-manager-loading',
31942             cn : [
31943                 {
31944                     tag : 'div',
31945                     tooltip : file.name,
31946                     cls : 'roo-document-manager-thumb',
31947                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31948                 }
31949             ]
31950
31951         });
31952
31953         this.xhr.open(this.method, this.url, true);
31954         
31955         var headers = {
31956             "Accept": "application/json",
31957             "Cache-Control": "no-cache",
31958             "X-Requested-With": "XMLHttpRequest"
31959         };
31960         
31961         for (var headerName in headers) {
31962             var headerValue = headers[headerName];
31963             if (headerValue) {
31964                 this.xhr.setRequestHeader(headerName, headerValue);
31965             }
31966         }
31967         
31968         var _this = this;
31969         
31970         this.xhr.onload = function()
31971         {
31972             _this.xhrOnLoad(_this.xhr);
31973         }
31974         
31975         this.xhr.onerror = function()
31976         {
31977             _this.xhrOnError(_this.xhr);
31978         }
31979         
31980         var formData = new FormData();
31981
31982         formData.append('returnHTML', 'NO');
31983         
31984         if(crop){
31985             formData.append('crop', crop);
31986         }
31987         
31988         formData.append(this.paramName, file, file.name);
31989         
31990         var options = {
31991             file : file, 
31992             manually : false
31993         };
31994         
31995         if(this.fireEvent('prepare', this, formData, options) != false){
31996             
31997             if(options.manually){
31998                 return;
31999             }
32000             
32001             this.xhr.send(formData);
32002             return;
32003         };
32004         
32005         this.uploadCancel();
32006     },
32007     
32008     uploadCancel : function()
32009     {
32010         if (this.xhr) {
32011             this.xhr.abort();
32012         }
32013         
32014         this.delegates = [];
32015         
32016         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
32017             el.remove();
32018         }, this);
32019         
32020         this.arrange();
32021     },
32022     
32023     renderPreview : function(file)
32024     {
32025         if(typeof(file.target) != 'undefined' && file.target){
32026             return file;
32027         }
32028         
32029         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
32030         
32031         var previewEl = this.managerEl.createChild({
32032             tag : 'div',
32033             cls : 'roo-document-manager-preview',
32034             cn : [
32035                 {
32036                     tag : 'div',
32037                     tooltip : file[this.toolTipName],
32038                     cls : 'roo-document-manager-thumb',
32039                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
32040                 },
32041                 {
32042                     tag : 'button',
32043                     cls : 'close',
32044                     html : '<i class="fa fa-times-circle"></i>'
32045                 }
32046             ]
32047         });
32048
32049         var close = previewEl.select('button.close', true).first();
32050
32051         close.on('click', this.onRemove, this, file);
32052
32053         file.target = previewEl;
32054
32055         var image = previewEl.select('img', true).first();
32056         
32057         var _this = this;
32058         
32059         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
32060         
32061         image.on('click', this.onClick, this, file);
32062         
32063         this.fireEvent('previewrendered', this, file);
32064         
32065         return file;
32066         
32067     },
32068     
32069     onPreviewLoad : function(file, image)
32070     {
32071         if(typeof(file.target) == 'undefined' || !file.target){
32072             return;
32073         }
32074         
32075         var width = image.dom.naturalWidth || image.dom.width;
32076         var height = image.dom.naturalHeight || image.dom.height;
32077         
32078         if(!this.previewResize) {
32079             return;
32080         }
32081         
32082         if(width > height){
32083             file.target.addClass('wide');
32084             return;
32085         }
32086         
32087         file.target.addClass('tall');
32088         return;
32089         
32090     },
32091     
32092     uploadFromSource : function(file, crop)
32093     {
32094         this.xhr = new XMLHttpRequest();
32095         
32096         this.managerEl.createChild({
32097             tag : 'div',
32098             cls : 'roo-document-manager-loading',
32099             cn : [
32100                 {
32101                     tag : 'div',
32102                     tooltip : file.name,
32103                     cls : 'roo-document-manager-thumb',
32104                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
32105                 }
32106             ]
32107
32108         });
32109
32110         this.xhr.open(this.method, this.url, true);
32111         
32112         var headers = {
32113             "Accept": "application/json",
32114             "Cache-Control": "no-cache",
32115             "X-Requested-With": "XMLHttpRequest"
32116         };
32117         
32118         for (var headerName in headers) {
32119             var headerValue = headers[headerName];
32120             if (headerValue) {
32121                 this.xhr.setRequestHeader(headerName, headerValue);
32122             }
32123         }
32124         
32125         var _this = this;
32126         
32127         this.xhr.onload = function()
32128         {
32129             _this.xhrOnLoad(_this.xhr);
32130         }
32131         
32132         this.xhr.onerror = function()
32133         {
32134             _this.xhrOnError(_this.xhr);
32135         }
32136         
32137         var formData = new FormData();
32138
32139         formData.append('returnHTML', 'NO');
32140         
32141         formData.append('crop', crop);
32142         
32143         if(typeof(file.filename) != 'undefined'){
32144             formData.append('filename', file.filename);
32145         }
32146         
32147         if(typeof(file.mimetype) != 'undefined'){
32148             formData.append('mimetype', file.mimetype);
32149         }
32150         
32151         Roo.log(formData);
32152         
32153         if(this.fireEvent('prepare', this, formData) != false){
32154             this.xhr.send(formData);
32155         };
32156     }
32157 });
32158
32159 /*
32160 * Licence: LGPL
32161 */
32162
32163 /**
32164  * @class Roo.bootstrap.DocumentViewer
32165  * @extends Roo.bootstrap.Component
32166  * Bootstrap DocumentViewer class
32167  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32168  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32169  * 
32170  * @constructor
32171  * Create a new DocumentViewer
32172  * @param {Object} config The config object
32173  */
32174
32175 Roo.bootstrap.DocumentViewer = function(config){
32176     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32177     
32178     this.addEvents({
32179         /**
32180          * @event initial
32181          * Fire after initEvent
32182          * @param {Roo.bootstrap.DocumentViewer} this
32183          */
32184         "initial" : true,
32185         /**
32186          * @event click
32187          * Fire after click
32188          * @param {Roo.bootstrap.DocumentViewer} this
32189          */
32190         "click" : true,
32191         /**
32192          * @event download
32193          * Fire after download button
32194          * @param {Roo.bootstrap.DocumentViewer} this
32195          */
32196         "download" : true,
32197         /**
32198          * @event trash
32199          * Fire after trash button
32200          * @param {Roo.bootstrap.DocumentViewer} this
32201          */
32202         "trash" : true
32203         
32204     });
32205 };
32206
32207 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32208     
32209     showDownload : true,
32210     
32211     showTrash : true,
32212     
32213     getAutoCreate : function()
32214     {
32215         var cfg = {
32216             tag : 'div',
32217             cls : 'roo-document-viewer',
32218             cn : [
32219                 {
32220                     tag : 'div',
32221                     cls : 'roo-document-viewer-body',
32222                     cn : [
32223                         {
32224                             tag : 'div',
32225                             cls : 'roo-document-viewer-thumb',
32226                             cn : [
32227                                 {
32228                                     tag : 'img',
32229                                     cls : 'roo-document-viewer-image'
32230                                 }
32231                             ]
32232                         }
32233                     ]
32234                 },
32235                 {
32236                     tag : 'div',
32237                     cls : 'roo-document-viewer-footer',
32238                     cn : {
32239                         tag : 'div',
32240                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32241                         cn : [
32242                             {
32243                                 tag : 'div',
32244                                 cls : 'btn-group roo-document-viewer-download',
32245                                 cn : [
32246                                     {
32247                                         tag : 'button',
32248                                         cls : 'btn btn-default',
32249                                         html : '<i class="fa fa-download"></i>'
32250                                     }
32251                                 ]
32252                             },
32253                             {
32254                                 tag : 'div',
32255                                 cls : 'btn-group roo-document-viewer-trash',
32256                                 cn : [
32257                                     {
32258                                         tag : 'button',
32259                                         cls : 'btn btn-default',
32260                                         html : '<i class="fa fa-trash"></i>'
32261                                     }
32262                                 ]
32263                             }
32264                         ]
32265                     }
32266                 }
32267             ]
32268         };
32269         
32270         return cfg;
32271     },
32272     
32273     initEvents : function()
32274     {
32275         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32276         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32277         
32278         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32279         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32280         
32281         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32282         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32283         
32284         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32285         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32286         
32287         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32288         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32289         
32290         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32291         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32292         
32293         this.bodyEl.on('click', this.onClick, this);
32294         this.downloadBtn.on('click', this.onDownload, this);
32295         this.trashBtn.on('click', this.onTrash, this);
32296         
32297         this.downloadBtn.hide();
32298         this.trashBtn.hide();
32299         
32300         if(this.showDownload){
32301             this.downloadBtn.show();
32302         }
32303         
32304         if(this.showTrash){
32305             this.trashBtn.show();
32306         }
32307         
32308         if(!this.showDownload && !this.showTrash) {
32309             this.footerEl.hide();
32310         }
32311         
32312     },
32313     
32314     initial : function()
32315     {
32316         this.fireEvent('initial', this);
32317         
32318     },
32319     
32320     onClick : function(e)
32321     {
32322         e.preventDefault();
32323         
32324         this.fireEvent('click', this);
32325     },
32326     
32327     onDownload : function(e)
32328     {
32329         e.preventDefault();
32330         
32331         this.fireEvent('download', this);
32332     },
32333     
32334     onTrash : function(e)
32335     {
32336         e.preventDefault();
32337         
32338         this.fireEvent('trash', this);
32339     }
32340     
32341 });
32342 /*
32343  * - LGPL
32344  *
32345  * nav progress bar
32346  * 
32347  */
32348
32349 /**
32350  * @class Roo.bootstrap.NavProgressBar
32351  * @extends Roo.bootstrap.Component
32352  * Bootstrap NavProgressBar class
32353  * 
32354  * @constructor
32355  * Create a new nav progress bar
32356  * @param {Object} config The config object
32357  */
32358
32359 Roo.bootstrap.NavProgressBar = function(config){
32360     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32361
32362     this.bullets = this.bullets || [];
32363    
32364 //    Roo.bootstrap.NavProgressBar.register(this);
32365      this.addEvents({
32366         /**
32367              * @event changed
32368              * Fires when the active item changes
32369              * @param {Roo.bootstrap.NavProgressBar} this
32370              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32371              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32372          */
32373         'changed': true
32374      });
32375     
32376 };
32377
32378 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32379     
32380     bullets : [],
32381     barItems : [],
32382     
32383     getAutoCreate : function()
32384     {
32385         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32386         
32387         cfg = {
32388             tag : 'div',
32389             cls : 'roo-navigation-bar-group',
32390             cn : [
32391                 {
32392                     tag : 'div',
32393                     cls : 'roo-navigation-top-bar'
32394                 },
32395                 {
32396                     tag : 'div',
32397                     cls : 'roo-navigation-bullets-bar',
32398                     cn : [
32399                         {
32400                             tag : 'ul',
32401                             cls : 'roo-navigation-bar'
32402                         }
32403                     ]
32404                 },
32405                 
32406                 {
32407                     tag : 'div',
32408                     cls : 'roo-navigation-bottom-bar'
32409                 }
32410             ]
32411             
32412         };
32413         
32414         return cfg;
32415         
32416     },
32417     
32418     initEvents: function() 
32419     {
32420         
32421     },
32422     
32423     onRender : function(ct, position) 
32424     {
32425         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32426         
32427         if(this.bullets.length){
32428             Roo.each(this.bullets, function(b){
32429                this.addItem(b);
32430             }, this);
32431         }
32432         
32433         this.format();
32434         
32435     },
32436     
32437     addItem : function(cfg)
32438     {
32439         var item = new Roo.bootstrap.NavProgressItem(cfg);
32440         
32441         item.parentId = this.id;
32442         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32443         
32444         if(cfg.html){
32445             var top = new Roo.bootstrap.Element({
32446                 tag : 'div',
32447                 cls : 'roo-navigation-bar-text'
32448             });
32449             
32450             var bottom = new Roo.bootstrap.Element({
32451                 tag : 'div',
32452                 cls : 'roo-navigation-bar-text'
32453             });
32454             
32455             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32456             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32457             
32458             var topText = new Roo.bootstrap.Element({
32459                 tag : 'span',
32460                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32461             });
32462             
32463             var bottomText = new Roo.bootstrap.Element({
32464                 tag : 'span',
32465                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32466             });
32467             
32468             topText.onRender(top.el, null);
32469             bottomText.onRender(bottom.el, null);
32470             
32471             item.topEl = top;
32472             item.bottomEl = bottom;
32473         }
32474         
32475         this.barItems.push(item);
32476         
32477         return item;
32478     },
32479     
32480     getActive : function()
32481     {
32482         var active = false;
32483         
32484         Roo.each(this.barItems, function(v){
32485             
32486             if (!v.isActive()) {
32487                 return;
32488             }
32489             
32490             active = v;
32491             return false;
32492             
32493         });
32494         
32495         return active;
32496     },
32497     
32498     setActiveItem : function(item)
32499     {
32500         var prev = false;
32501         
32502         Roo.each(this.barItems, function(v){
32503             if (v.rid == item.rid) {
32504                 return ;
32505             }
32506             
32507             if (v.isActive()) {
32508                 v.setActive(false);
32509                 prev = v;
32510             }
32511         });
32512
32513         item.setActive(true);
32514         
32515         this.fireEvent('changed', this, item, prev);
32516     },
32517     
32518     getBarItem: function(rid)
32519     {
32520         var ret = false;
32521         
32522         Roo.each(this.barItems, function(e) {
32523             if (e.rid != rid) {
32524                 return;
32525             }
32526             
32527             ret =  e;
32528             return false;
32529         });
32530         
32531         return ret;
32532     },
32533     
32534     indexOfItem : function(item)
32535     {
32536         var index = false;
32537         
32538         Roo.each(this.barItems, function(v, i){
32539             
32540             if (v.rid != item.rid) {
32541                 return;
32542             }
32543             
32544             index = i;
32545             return false
32546         });
32547         
32548         return index;
32549     },
32550     
32551     setActiveNext : function()
32552     {
32553         var i = this.indexOfItem(this.getActive());
32554         
32555         if (i > this.barItems.length) {
32556             return;
32557         }
32558         
32559         this.setActiveItem(this.barItems[i+1]);
32560     },
32561     
32562     setActivePrev : function()
32563     {
32564         var i = this.indexOfItem(this.getActive());
32565         
32566         if (i  < 1) {
32567             return;
32568         }
32569         
32570         this.setActiveItem(this.barItems[i-1]);
32571     },
32572     
32573     format : function()
32574     {
32575         if(!this.barItems.length){
32576             return;
32577         }
32578      
32579         var width = 100 / this.barItems.length;
32580         
32581         Roo.each(this.barItems, function(i){
32582             i.el.setStyle('width', width + '%');
32583             i.topEl.el.setStyle('width', width + '%');
32584             i.bottomEl.el.setStyle('width', width + '%');
32585         }, this);
32586         
32587     }
32588     
32589 });
32590 /*
32591  * - LGPL
32592  *
32593  * Nav Progress Item
32594  * 
32595  */
32596
32597 /**
32598  * @class Roo.bootstrap.NavProgressItem
32599  * @extends Roo.bootstrap.Component
32600  * Bootstrap NavProgressItem class
32601  * @cfg {String} rid the reference id
32602  * @cfg {Boolean} active (true|false) Is item active default false
32603  * @cfg {Boolean} disabled (true|false) Is item active default false
32604  * @cfg {String} html
32605  * @cfg {String} position (top|bottom) text position default bottom
32606  * @cfg {String} icon show icon instead of number
32607  * 
32608  * @constructor
32609  * Create a new NavProgressItem
32610  * @param {Object} config The config object
32611  */
32612 Roo.bootstrap.NavProgressItem = function(config){
32613     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32614     this.addEvents({
32615         // raw events
32616         /**
32617          * @event click
32618          * The raw click event for the entire grid.
32619          * @param {Roo.bootstrap.NavProgressItem} this
32620          * @param {Roo.EventObject} e
32621          */
32622         "click" : true
32623     });
32624    
32625 };
32626
32627 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32628     
32629     rid : '',
32630     active : false,
32631     disabled : false,
32632     html : '',
32633     position : 'bottom',
32634     icon : false,
32635     
32636     getAutoCreate : function()
32637     {
32638         var iconCls = 'roo-navigation-bar-item-icon';
32639         
32640         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32641         
32642         var cfg = {
32643             tag: 'li',
32644             cls: 'roo-navigation-bar-item',
32645             cn : [
32646                 {
32647                     tag : 'i',
32648                     cls : iconCls
32649                 }
32650             ]
32651         };
32652         
32653         if(this.active){
32654             cfg.cls += ' active';
32655         }
32656         if(this.disabled){
32657             cfg.cls += ' disabled';
32658         }
32659         
32660         return cfg;
32661     },
32662     
32663     disable : function()
32664     {
32665         this.setDisabled(true);
32666     },
32667     
32668     enable : function()
32669     {
32670         this.setDisabled(false);
32671     },
32672     
32673     initEvents: function() 
32674     {
32675         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32676         
32677         this.iconEl.on('click', this.onClick, this);
32678     },
32679     
32680     onClick : function(e)
32681     {
32682         e.preventDefault();
32683         
32684         if(this.disabled){
32685             return;
32686         }
32687         
32688         if(this.fireEvent('click', this, e) === false){
32689             return;
32690         };
32691         
32692         this.parent().setActiveItem(this);
32693     },
32694     
32695     isActive: function () 
32696     {
32697         return this.active;
32698     },
32699     
32700     setActive : function(state)
32701     {
32702         if(this.active == state){
32703             return;
32704         }
32705         
32706         this.active = state;
32707         
32708         if (state) {
32709             this.el.addClass('active');
32710             return;
32711         }
32712         
32713         this.el.removeClass('active');
32714         
32715         return;
32716     },
32717     
32718     setDisabled : function(state)
32719     {
32720         if(this.disabled == state){
32721             return;
32722         }
32723         
32724         this.disabled = state;
32725         
32726         if (state) {
32727             this.el.addClass('disabled');
32728             return;
32729         }
32730         
32731         this.el.removeClass('disabled');
32732     },
32733     
32734     tooltipEl : function()
32735     {
32736         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32737     }
32738 });
32739  
32740
32741  /*
32742  * - LGPL
32743  *
32744  * FieldLabel
32745  * 
32746  */
32747
32748 /**
32749  * @class Roo.bootstrap.FieldLabel
32750  * @extends Roo.bootstrap.Component
32751  * Bootstrap FieldLabel class
32752  * @cfg {String} html contents of the element
32753  * @cfg {String} tag tag of the element default label
32754  * @cfg {String} cls class of the element
32755  * @cfg {String} target label target 
32756  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32757  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32758  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32759  * @cfg {String} iconTooltip default "This field is required"
32760  * @cfg {String} indicatorpos (left|right) default left
32761  * 
32762  * @constructor
32763  * Create a new FieldLabel
32764  * @param {Object} config The config object
32765  */
32766
32767 Roo.bootstrap.FieldLabel = function(config){
32768     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32769     
32770     this.addEvents({
32771             /**
32772              * @event invalid
32773              * Fires after the field has been marked as invalid.
32774              * @param {Roo.form.FieldLabel} this
32775              * @param {String} msg The validation message
32776              */
32777             invalid : true,
32778             /**
32779              * @event valid
32780              * Fires after the field has been validated with no errors.
32781              * @param {Roo.form.FieldLabel} this
32782              */
32783             valid : true
32784         });
32785 };
32786
32787 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32788     
32789     tag: 'label',
32790     cls: '',
32791     html: '',
32792     target: '',
32793     allowBlank : true,
32794     invalidClass : 'has-warning',
32795     validClass : 'has-success',
32796     iconTooltip : 'This field is required',
32797     indicatorpos : 'left',
32798     
32799     getAutoCreate : function(){
32800         
32801         var cls = "";
32802         if (!this.allowBlank) {
32803             cls  = "visible";
32804         }
32805         
32806         var cfg = {
32807             tag : this.tag,
32808             cls : 'roo-bootstrap-field-label ' + this.cls,
32809             for : this.target,
32810             cn : [
32811                 {
32812                     tag : 'i',
32813                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32814                     tooltip : this.iconTooltip
32815                 },
32816                 {
32817                     tag : 'span',
32818                     html : this.html
32819                 }
32820             ] 
32821         };
32822         
32823         if(this.indicatorpos == 'right'){
32824             var cfg = {
32825                 tag : this.tag,
32826                 cls : 'roo-bootstrap-field-label ' + this.cls,
32827                 for : this.target,
32828                 cn : [
32829                     {
32830                         tag : 'span',
32831                         html : this.html
32832                     },
32833                     {
32834                         tag : 'i',
32835                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32836                         tooltip : this.iconTooltip
32837                     }
32838                 ] 
32839             };
32840         }
32841         
32842         return cfg;
32843     },
32844     
32845     initEvents: function() 
32846     {
32847         Roo.bootstrap.Element.superclass.initEvents.call(this);
32848         
32849         this.indicator = this.indicatorEl();
32850         
32851         if(this.indicator){
32852             this.indicator.removeClass('visible');
32853             this.indicator.addClass('invisible');
32854         }
32855         
32856         Roo.bootstrap.FieldLabel.register(this);
32857     },
32858     
32859     indicatorEl : function()
32860     {
32861         var indicator = this.el.select('i.roo-required-indicator',true).first();
32862         
32863         if(!indicator){
32864             return false;
32865         }
32866         
32867         return indicator;
32868         
32869     },
32870     
32871     /**
32872      * Mark this field as valid
32873      */
32874     markValid : function()
32875     {
32876         if(this.indicator){
32877             this.indicator.removeClass('visible');
32878             this.indicator.addClass('invisible');
32879         }
32880         if (Roo.bootstrap.version == 3) {
32881             this.el.removeClass(this.invalidClass);
32882             this.el.addClass(this.validClass);
32883         } else {
32884             this.el.removeClass('is-invalid');
32885             this.el.addClass('is-valid');
32886         }
32887         
32888         
32889         this.fireEvent('valid', this);
32890     },
32891     
32892     /**
32893      * Mark this field as invalid
32894      * @param {String} msg The validation message
32895      */
32896     markInvalid : function(msg)
32897     {
32898         if(this.indicator){
32899             this.indicator.removeClass('invisible');
32900             this.indicator.addClass('visible');
32901         }
32902           if (Roo.bootstrap.version == 3) {
32903             this.el.removeClass(this.validClass);
32904             this.el.addClass(this.invalidClass);
32905         } else {
32906             this.el.removeClass('is-valid');
32907             this.el.addClass('is-invalid');
32908         }
32909         
32910         
32911         this.fireEvent('invalid', this, msg);
32912     }
32913     
32914    
32915 });
32916
32917 Roo.apply(Roo.bootstrap.FieldLabel, {
32918     
32919     groups: {},
32920     
32921      /**
32922     * register a FieldLabel Group
32923     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32924     */
32925     register : function(label)
32926     {
32927         if(this.groups.hasOwnProperty(label.target)){
32928             return;
32929         }
32930      
32931         this.groups[label.target] = label;
32932         
32933     },
32934     /**
32935     * fetch a FieldLabel Group based on the target
32936     * @param {string} target
32937     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32938     */
32939     get: function(target) {
32940         if (typeof(this.groups[target]) == 'undefined') {
32941             return false;
32942         }
32943         
32944         return this.groups[target] ;
32945     }
32946 });
32947
32948  
32949
32950  /*
32951  * - LGPL
32952  *
32953  * page DateSplitField.
32954  * 
32955  */
32956
32957
32958 /**
32959  * @class Roo.bootstrap.DateSplitField
32960  * @extends Roo.bootstrap.Component
32961  * Bootstrap DateSplitField class
32962  * @cfg {string} fieldLabel - the label associated
32963  * @cfg {Number} labelWidth set the width of label (0-12)
32964  * @cfg {String} labelAlign (top|left)
32965  * @cfg {Boolean} dayAllowBlank (true|false) default false
32966  * @cfg {Boolean} monthAllowBlank (true|false) default false
32967  * @cfg {Boolean} yearAllowBlank (true|false) default false
32968  * @cfg {string} dayPlaceholder 
32969  * @cfg {string} monthPlaceholder
32970  * @cfg {string} yearPlaceholder
32971  * @cfg {string} dayFormat default 'd'
32972  * @cfg {string} monthFormat default 'm'
32973  * @cfg {string} yearFormat default 'Y'
32974  * @cfg {Number} labellg set the width of label (1-12)
32975  * @cfg {Number} labelmd set the width of label (1-12)
32976  * @cfg {Number} labelsm set the width of label (1-12)
32977  * @cfg {Number} labelxs set the width of label (1-12)
32978
32979  *     
32980  * @constructor
32981  * Create a new DateSplitField
32982  * @param {Object} config The config object
32983  */
32984
32985 Roo.bootstrap.DateSplitField = function(config){
32986     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32987     
32988     this.addEvents({
32989         // raw events
32990          /**
32991          * @event years
32992          * getting the data of years
32993          * @param {Roo.bootstrap.DateSplitField} this
32994          * @param {Object} years
32995          */
32996         "years" : true,
32997         /**
32998          * @event days
32999          * getting the data of days
33000          * @param {Roo.bootstrap.DateSplitField} this
33001          * @param {Object} days
33002          */
33003         "days" : true,
33004         /**
33005          * @event invalid
33006          * Fires after the field has been marked as invalid.
33007          * @param {Roo.form.Field} this
33008          * @param {String} msg The validation message
33009          */
33010         invalid : true,
33011        /**
33012          * @event valid
33013          * Fires after the field has been validated with no errors.
33014          * @param {Roo.form.Field} this
33015          */
33016         valid : true
33017     });
33018 };
33019
33020 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
33021     
33022     fieldLabel : '',
33023     labelAlign : 'top',
33024     labelWidth : 3,
33025     dayAllowBlank : false,
33026     monthAllowBlank : false,
33027     yearAllowBlank : false,
33028     dayPlaceholder : '',
33029     monthPlaceholder : '',
33030     yearPlaceholder : '',
33031     dayFormat : 'd',
33032     monthFormat : 'm',
33033     yearFormat : 'Y',
33034     isFormField : true,
33035     labellg : 0,
33036     labelmd : 0,
33037     labelsm : 0,
33038     labelxs : 0,
33039     
33040     getAutoCreate : function()
33041     {
33042         var cfg = {
33043             tag : 'div',
33044             cls : 'row roo-date-split-field-group',
33045             cn : [
33046                 {
33047                     tag : 'input',
33048                     type : 'hidden',
33049                     cls : 'form-hidden-field roo-date-split-field-group-value',
33050                     name : this.name
33051                 }
33052             ]
33053         };
33054         
33055         var labelCls = 'col-md-12';
33056         var contentCls = 'col-md-4';
33057         
33058         if(this.fieldLabel){
33059             
33060             var label = {
33061                 tag : 'div',
33062                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
33063                 cn : [
33064                     {
33065                         tag : 'label',
33066                         html : this.fieldLabel
33067                     }
33068                 ]
33069             };
33070             
33071             if(this.labelAlign == 'left'){
33072             
33073                 if(this.labelWidth > 12){
33074                     label.style = "width: " + this.labelWidth + 'px';
33075                 }
33076
33077                 if(this.labelWidth < 13 && this.labelmd == 0){
33078                     this.labelmd = this.labelWidth;
33079                 }
33080
33081                 if(this.labellg > 0){
33082                     labelCls = ' col-lg-' + this.labellg;
33083                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
33084                 }
33085
33086                 if(this.labelmd > 0){
33087                     labelCls = ' col-md-' + this.labelmd;
33088                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
33089                 }
33090
33091                 if(this.labelsm > 0){
33092                     labelCls = ' col-sm-' + this.labelsm;
33093                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
33094                 }
33095
33096                 if(this.labelxs > 0){
33097                     labelCls = ' col-xs-' + this.labelxs;
33098                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
33099                 }
33100             }
33101             
33102             label.cls += ' ' + labelCls;
33103             
33104             cfg.cn.push(label);
33105         }
33106         
33107         Roo.each(['day', 'month', 'year'], function(t){
33108             cfg.cn.push({
33109                 tag : 'div',
33110                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
33111             });
33112         }, this);
33113         
33114         return cfg;
33115     },
33116     
33117     inputEl: function ()
33118     {
33119         return this.el.select('.roo-date-split-field-group-value', true).first();
33120     },
33121     
33122     onRender : function(ct, position) 
33123     {
33124         var _this = this;
33125         
33126         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
33127         
33128         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
33129         
33130         this.dayField = new Roo.bootstrap.ComboBox({
33131             allowBlank : this.dayAllowBlank,
33132             alwaysQuery : true,
33133             displayField : 'value',
33134             editable : false,
33135             fieldLabel : '',
33136             forceSelection : true,
33137             mode : 'local',
33138             placeholder : this.dayPlaceholder,
33139             selectOnFocus : true,
33140             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33141             triggerAction : 'all',
33142             typeAhead : true,
33143             valueField : 'value',
33144             store : new Roo.data.SimpleStore({
33145                 data : (function() {    
33146                     var days = [];
33147                     _this.fireEvent('days', _this, days);
33148                     return days;
33149                 })(),
33150                 fields : [ 'value' ]
33151             }),
33152             listeners : {
33153                 select : function (_self, record, index)
33154                 {
33155                     _this.setValue(_this.getValue());
33156                 }
33157             }
33158         });
33159
33160         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33161         
33162         this.monthField = new Roo.bootstrap.MonthField({
33163             after : '<i class=\"fa fa-calendar\"></i>',
33164             allowBlank : this.monthAllowBlank,
33165             placeholder : this.monthPlaceholder,
33166             readOnly : true,
33167             listeners : {
33168                 render : function (_self)
33169                 {
33170                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33171                         e.preventDefault();
33172                         _self.focus();
33173                     });
33174                 },
33175                 select : function (_self, oldvalue, newvalue)
33176                 {
33177                     _this.setValue(_this.getValue());
33178                 }
33179             }
33180         });
33181         
33182         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33183         
33184         this.yearField = new Roo.bootstrap.ComboBox({
33185             allowBlank : this.yearAllowBlank,
33186             alwaysQuery : true,
33187             displayField : 'value',
33188             editable : false,
33189             fieldLabel : '',
33190             forceSelection : true,
33191             mode : 'local',
33192             placeholder : this.yearPlaceholder,
33193             selectOnFocus : true,
33194             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33195             triggerAction : 'all',
33196             typeAhead : true,
33197             valueField : 'value',
33198             store : new Roo.data.SimpleStore({
33199                 data : (function() {
33200                     var years = [];
33201                     _this.fireEvent('years', _this, years);
33202                     return years;
33203                 })(),
33204                 fields : [ 'value' ]
33205             }),
33206             listeners : {
33207                 select : function (_self, record, index)
33208                 {
33209                     _this.setValue(_this.getValue());
33210                 }
33211             }
33212         });
33213
33214         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33215     },
33216     
33217     setValue : function(v, format)
33218     {
33219         this.inputEl.dom.value = v;
33220         
33221         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33222         
33223         var d = Date.parseDate(v, f);
33224         
33225         if(!d){
33226             this.validate();
33227             return;
33228         }
33229         
33230         this.setDay(d.format(this.dayFormat));
33231         this.setMonth(d.format(this.monthFormat));
33232         this.setYear(d.format(this.yearFormat));
33233         
33234         this.validate();
33235         
33236         return;
33237     },
33238     
33239     setDay : function(v)
33240     {
33241         this.dayField.setValue(v);
33242         this.inputEl.dom.value = this.getValue();
33243         this.validate();
33244         return;
33245     },
33246     
33247     setMonth : function(v)
33248     {
33249         this.monthField.setValue(v, true);
33250         this.inputEl.dom.value = this.getValue();
33251         this.validate();
33252         return;
33253     },
33254     
33255     setYear : function(v)
33256     {
33257         this.yearField.setValue(v);
33258         this.inputEl.dom.value = this.getValue();
33259         this.validate();
33260         return;
33261     },
33262     
33263     getDay : function()
33264     {
33265         return this.dayField.getValue();
33266     },
33267     
33268     getMonth : function()
33269     {
33270         return this.monthField.getValue();
33271     },
33272     
33273     getYear : function()
33274     {
33275         return this.yearField.getValue();
33276     },
33277     
33278     getValue : function()
33279     {
33280         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33281         
33282         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33283         
33284         return date;
33285     },
33286     
33287     reset : function()
33288     {
33289         this.setDay('');
33290         this.setMonth('');
33291         this.setYear('');
33292         this.inputEl.dom.value = '';
33293         this.validate();
33294         return;
33295     },
33296     
33297     validate : function()
33298     {
33299         var d = this.dayField.validate();
33300         var m = this.monthField.validate();
33301         var y = this.yearField.validate();
33302         
33303         var valid = true;
33304         
33305         if(
33306                 (!this.dayAllowBlank && !d) ||
33307                 (!this.monthAllowBlank && !m) ||
33308                 (!this.yearAllowBlank && !y)
33309         ){
33310             valid = false;
33311         }
33312         
33313         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33314             return valid;
33315         }
33316         
33317         if(valid){
33318             this.markValid();
33319             return valid;
33320         }
33321         
33322         this.markInvalid();
33323         
33324         return valid;
33325     },
33326     
33327     markValid : function()
33328     {
33329         
33330         var label = this.el.select('label', true).first();
33331         var icon = this.el.select('i.fa-star', true).first();
33332
33333         if(label && icon){
33334             icon.remove();
33335         }
33336         
33337         this.fireEvent('valid', this);
33338     },
33339     
33340      /**
33341      * Mark this field as invalid
33342      * @param {String} msg The validation message
33343      */
33344     markInvalid : function(msg)
33345     {
33346         
33347         var label = this.el.select('label', true).first();
33348         var icon = this.el.select('i.fa-star', true).first();
33349
33350         if(label && !icon){
33351             this.el.select('.roo-date-split-field-label', true).createChild({
33352                 tag : 'i',
33353                 cls : 'text-danger fa fa-lg fa-star',
33354                 tooltip : 'This field is required',
33355                 style : 'margin-right:5px;'
33356             }, label, true);
33357         }
33358         
33359         this.fireEvent('invalid', this, msg);
33360     },
33361     
33362     clearInvalid : function()
33363     {
33364         var label = this.el.select('label', true).first();
33365         var icon = this.el.select('i.fa-star', true).first();
33366
33367         if(label && icon){
33368             icon.remove();
33369         }
33370         
33371         this.fireEvent('valid', this);
33372     },
33373     
33374     getName: function()
33375     {
33376         return this.name;
33377     }
33378     
33379 });
33380
33381  /**
33382  *
33383  * This is based on 
33384  * http://masonry.desandro.com
33385  *
33386  * The idea is to render all the bricks based on vertical width...
33387  *
33388  * The original code extends 'outlayer' - we might need to use that....
33389  * 
33390  */
33391
33392
33393 /**
33394  * @class Roo.bootstrap.LayoutMasonry
33395  * @extends Roo.bootstrap.Component
33396  * Bootstrap Layout Masonry class
33397  * 
33398  * @constructor
33399  * Create a new Element
33400  * @param {Object} config The config object
33401  */
33402
33403 Roo.bootstrap.LayoutMasonry = function(config){
33404     
33405     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33406     
33407     this.bricks = [];
33408     
33409     Roo.bootstrap.LayoutMasonry.register(this);
33410     
33411     this.addEvents({
33412         // raw events
33413         /**
33414          * @event layout
33415          * Fire after layout the items
33416          * @param {Roo.bootstrap.LayoutMasonry} this
33417          * @param {Roo.EventObject} e
33418          */
33419         "layout" : true
33420     });
33421     
33422 };
33423
33424 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33425     
33426     /**
33427      * @cfg {Boolean} isLayoutInstant = no animation?
33428      */   
33429     isLayoutInstant : false, // needed?
33430    
33431     /**
33432      * @cfg {Number} boxWidth  width of the columns
33433      */   
33434     boxWidth : 450,
33435     
33436       /**
33437      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33438      */   
33439     boxHeight : 0,
33440     
33441     /**
33442      * @cfg {Number} padWidth padding below box..
33443      */   
33444     padWidth : 10, 
33445     
33446     /**
33447      * @cfg {Number} gutter gutter width..
33448      */   
33449     gutter : 10,
33450     
33451      /**
33452      * @cfg {Number} maxCols maximum number of columns
33453      */   
33454     
33455     maxCols: 0,
33456     
33457     /**
33458      * @cfg {Boolean} isAutoInitial defalut true
33459      */   
33460     isAutoInitial : true, 
33461     
33462     containerWidth: 0,
33463     
33464     /**
33465      * @cfg {Boolean} isHorizontal defalut false
33466      */   
33467     isHorizontal : false, 
33468
33469     currentSize : null,
33470     
33471     tag: 'div',
33472     
33473     cls: '',
33474     
33475     bricks: null, //CompositeElement
33476     
33477     cols : 1,
33478     
33479     _isLayoutInited : false,
33480     
33481 //    isAlternative : false, // only use for vertical layout...
33482     
33483     /**
33484      * @cfg {Number} alternativePadWidth padding below box..
33485      */   
33486     alternativePadWidth : 50,
33487     
33488     selectedBrick : [],
33489     
33490     getAutoCreate : function(){
33491         
33492         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33493         
33494         var cfg = {
33495             tag: this.tag,
33496             cls: 'blog-masonary-wrapper ' + this.cls,
33497             cn : {
33498                 cls : 'mas-boxes masonary'
33499             }
33500         };
33501         
33502         return cfg;
33503     },
33504     
33505     getChildContainer: function( )
33506     {
33507         if (this.boxesEl) {
33508             return this.boxesEl;
33509         }
33510         
33511         this.boxesEl = this.el.select('.mas-boxes').first();
33512         
33513         return this.boxesEl;
33514     },
33515     
33516     
33517     initEvents : function()
33518     {
33519         var _this = this;
33520         
33521         if(this.isAutoInitial){
33522             Roo.log('hook children rendered');
33523             this.on('childrenrendered', function() {
33524                 Roo.log('children rendered');
33525                 _this.initial();
33526             } ,this);
33527         }
33528     },
33529     
33530     initial : function()
33531     {
33532         this.selectedBrick = [];
33533         
33534         this.currentSize = this.el.getBox(true);
33535         
33536         Roo.EventManager.onWindowResize(this.resize, this); 
33537
33538         if(!this.isAutoInitial){
33539             this.layout();
33540             return;
33541         }
33542         
33543         this.layout();
33544         
33545         return;
33546         //this.layout.defer(500,this);
33547         
33548     },
33549     
33550     resize : function()
33551     {
33552         var cs = this.el.getBox(true);
33553         
33554         if (
33555                 this.currentSize.width == cs.width && 
33556                 this.currentSize.x == cs.x && 
33557                 this.currentSize.height == cs.height && 
33558                 this.currentSize.y == cs.y 
33559         ) {
33560             Roo.log("no change in with or X or Y");
33561             return;
33562         }
33563         
33564         this.currentSize = cs;
33565         
33566         this.layout();
33567         
33568     },
33569     
33570     layout : function()
33571     {   
33572         this._resetLayout();
33573         
33574         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33575         
33576         this.layoutItems( isInstant );
33577       
33578         this._isLayoutInited = true;
33579         
33580         this.fireEvent('layout', this);
33581         
33582     },
33583     
33584     _resetLayout : function()
33585     {
33586         if(this.isHorizontal){
33587             this.horizontalMeasureColumns();
33588             return;
33589         }
33590         
33591         this.verticalMeasureColumns();
33592         
33593     },
33594     
33595     verticalMeasureColumns : function()
33596     {
33597         this.getContainerWidth();
33598         
33599 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33600 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33601 //            return;
33602 //        }
33603         
33604         var boxWidth = this.boxWidth + this.padWidth;
33605         
33606         if(this.containerWidth < this.boxWidth){
33607             boxWidth = this.containerWidth
33608         }
33609         
33610         var containerWidth = this.containerWidth;
33611         
33612         var cols = Math.floor(containerWidth / boxWidth);
33613         
33614         this.cols = Math.max( cols, 1 );
33615         
33616         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33617         
33618         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33619         
33620         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33621         
33622         this.colWidth = boxWidth + avail - this.padWidth;
33623         
33624         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33625         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33626     },
33627     
33628     horizontalMeasureColumns : function()
33629     {
33630         this.getContainerWidth();
33631         
33632         var boxWidth = this.boxWidth;
33633         
33634         if(this.containerWidth < boxWidth){
33635             boxWidth = this.containerWidth;
33636         }
33637         
33638         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33639         
33640         this.el.setHeight(boxWidth);
33641         
33642     },
33643     
33644     getContainerWidth : function()
33645     {
33646         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33647     },
33648     
33649     layoutItems : function( isInstant )
33650     {
33651         Roo.log(this.bricks);
33652         
33653         var items = Roo.apply([], this.bricks);
33654         
33655         if(this.isHorizontal){
33656             this._horizontalLayoutItems( items , isInstant );
33657             return;
33658         }
33659         
33660 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33661 //            this._verticalAlternativeLayoutItems( items , isInstant );
33662 //            return;
33663 //        }
33664         
33665         this._verticalLayoutItems( items , isInstant );
33666         
33667     },
33668     
33669     _verticalLayoutItems : function ( items , isInstant)
33670     {
33671         if ( !items || !items.length ) {
33672             return;
33673         }
33674         
33675         var standard = [
33676             ['xs', 'xs', 'xs', 'tall'],
33677             ['xs', 'xs', 'tall'],
33678             ['xs', 'xs', 'sm'],
33679             ['xs', 'xs', 'xs'],
33680             ['xs', 'tall'],
33681             ['xs', 'sm'],
33682             ['xs', 'xs'],
33683             ['xs'],
33684             
33685             ['sm', 'xs', 'xs'],
33686             ['sm', 'xs'],
33687             ['sm'],
33688             
33689             ['tall', 'xs', 'xs', 'xs'],
33690             ['tall', 'xs', 'xs'],
33691             ['tall', 'xs'],
33692             ['tall']
33693             
33694         ];
33695         
33696         var queue = [];
33697         
33698         var boxes = [];
33699         
33700         var box = [];
33701         
33702         Roo.each(items, function(item, k){
33703             
33704             switch (item.size) {
33705                 // these layouts take up a full box,
33706                 case 'md' :
33707                 case 'md-left' :
33708                 case 'md-right' :
33709                 case 'wide' :
33710                     
33711                     if(box.length){
33712                         boxes.push(box);
33713                         box = [];
33714                     }
33715                     
33716                     boxes.push([item]);
33717                     
33718                     break;
33719                     
33720                 case 'xs' :
33721                 case 'sm' :
33722                 case 'tall' :
33723                     
33724                     box.push(item);
33725                     
33726                     break;
33727                 default :
33728                     break;
33729                     
33730             }
33731             
33732         }, this);
33733         
33734         if(box.length){
33735             boxes.push(box);
33736             box = [];
33737         }
33738         
33739         var filterPattern = function(box, length)
33740         {
33741             if(!box.length){
33742                 return;
33743             }
33744             
33745             var match = false;
33746             
33747             var pattern = box.slice(0, length);
33748             
33749             var format = [];
33750             
33751             Roo.each(pattern, function(i){
33752                 format.push(i.size);
33753             }, this);
33754             
33755             Roo.each(standard, function(s){
33756                 
33757                 if(String(s) != String(format)){
33758                     return;
33759                 }
33760                 
33761                 match = true;
33762                 return false;
33763                 
33764             }, this);
33765             
33766             if(!match && length == 1){
33767                 return;
33768             }
33769             
33770             if(!match){
33771                 filterPattern(box, length - 1);
33772                 return;
33773             }
33774                 
33775             queue.push(pattern);
33776
33777             box = box.slice(length, box.length);
33778
33779             filterPattern(box, 4);
33780
33781             return;
33782             
33783         }
33784         
33785         Roo.each(boxes, function(box, k){
33786             
33787             if(!box.length){
33788                 return;
33789             }
33790             
33791             if(box.length == 1){
33792                 queue.push(box);
33793                 return;
33794             }
33795             
33796             filterPattern(box, 4);
33797             
33798         }, this);
33799         
33800         this._processVerticalLayoutQueue( queue, isInstant );
33801         
33802     },
33803     
33804 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33805 //    {
33806 //        if ( !items || !items.length ) {
33807 //            return;
33808 //        }
33809 //
33810 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33811 //        
33812 //    },
33813     
33814     _horizontalLayoutItems : function ( items , isInstant)
33815     {
33816         if ( !items || !items.length || items.length < 3) {
33817             return;
33818         }
33819         
33820         items.reverse();
33821         
33822         var eItems = items.slice(0, 3);
33823         
33824         items = items.slice(3, items.length);
33825         
33826         var standard = [
33827             ['xs', 'xs', 'xs', 'wide'],
33828             ['xs', 'xs', 'wide'],
33829             ['xs', 'xs', 'sm'],
33830             ['xs', 'xs', 'xs'],
33831             ['xs', 'wide'],
33832             ['xs', 'sm'],
33833             ['xs', 'xs'],
33834             ['xs'],
33835             
33836             ['sm', 'xs', 'xs'],
33837             ['sm', 'xs'],
33838             ['sm'],
33839             
33840             ['wide', 'xs', 'xs', 'xs'],
33841             ['wide', 'xs', 'xs'],
33842             ['wide', 'xs'],
33843             ['wide'],
33844             
33845             ['wide-thin']
33846         ];
33847         
33848         var queue = [];
33849         
33850         var boxes = [];
33851         
33852         var box = [];
33853         
33854         Roo.each(items, function(item, k){
33855             
33856             switch (item.size) {
33857                 case 'md' :
33858                 case 'md-left' :
33859                 case 'md-right' :
33860                 case 'tall' :
33861                     
33862                     if(box.length){
33863                         boxes.push(box);
33864                         box = [];
33865                     }
33866                     
33867                     boxes.push([item]);
33868                     
33869                     break;
33870                     
33871                 case 'xs' :
33872                 case 'sm' :
33873                 case 'wide' :
33874                 case 'wide-thin' :
33875                     
33876                     box.push(item);
33877                     
33878                     break;
33879                 default :
33880                     break;
33881                     
33882             }
33883             
33884         }, this);
33885         
33886         if(box.length){
33887             boxes.push(box);
33888             box = [];
33889         }
33890         
33891         var filterPattern = function(box, length)
33892         {
33893             if(!box.length){
33894                 return;
33895             }
33896             
33897             var match = false;
33898             
33899             var pattern = box.slice(0, length);
33900             
33901             var format = [];
33902             
33903             Roo.each(pattern, function(i){
33904                 format.push(i.size);
33905             }, this);
33906             
33907             Roo.each(standard, function(s){
33908                 
33909                 if(String(s) != String(format)){
33910                     return;
33911                 }
33912                 
33913                 match = true;
33914                 return false;
33915                 
33916             }, this);
33917             
33918             if(!match && length == 1){
33919                 return;
33920             }
33921             
33922             if(!match){
33923                 filterPattern(box, length - 1);
33924                 return;
33925             }
33926                 
33927             queue.push(pattern);
33928
33929             box = box.slice(length, box.length);
33930
33931             filterPattern(box, 4);
33932
33933             return;
33934             
33935         }
33936         
33937         Roo.each(boxes, function(box, k){
33938             
33939             if(!box.length){
33940                 return;
33941             }
33942             
33943             if(box.length == 1){
33944                 queue.push(box);
33945                 return;
33946             }
33947             
33948             filterPattern(box, 4);
33949             
33950         }, this);
33951         
33952         
33953         var prune = [];
33954         
33955         var pos = this.el.getBox(true);
33956         
33957         var minX = pos.x;
33958         
33959         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33960         
33961         var hit_end = false;
33962         
33963         Roo.each(queue, function(box){
33964             
33965             if(hit_end){
33966                 
33967                 Roo.each(box, function(b){
33968                 
33969                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33970                     b.el.hide();
33971
33972                 }, this);
33973
33974                 return;
33975             }
33976             
33977             var mx = 0;
33978             
33979             Roo.each(box, function(b){
33980                 
33981                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33982                 b.el.show();
33983
33984                 mx = Math.max(mx, b.x);
33985                 
33986             }, this);
33987             
33988             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33989             
33990             if(maxX < minX){
33991                 
33992                 Roo.each(box, function(b){
33993                 
33994                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33995                     b.el.hide();
33996                     
33997                 }, this);
33998                 
33999                 hit_end = true;
34000                 
34001                 return;
34002             }
34003             
34004             prune.push(box);
34005             
34006         }, this);
34007         
34008         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
34009     },
34010     
34011     /** Sets position of item in DOM
34012     * @param {Element} item
34013     * @param {Number} x - horizontal position
34014     * @param {Number} y - vertical position
34015     * @param {Boolean} isInstant - disables transitions
34016     */
34017     _processVerticalLayoutQueue : function( queue, isInstant )
34018     {
34019         var pos = this.el.getBox(true);
34020         var x = pos.x;
34021         var y = pos.y;
34022         var maxY = [];
34023         
34024         for (var i = 0; i < this.cols; i++){
34025             maxY[i] = pos.y;
34026         }
34027         
34028         Roo.each(queue, function(box, k){
34029             
34030             var col = k % this.cols;
34031             
34032             Roo.each(box, function(b,kk){
34033                 
34034                 b.el.position('absolute');
34035                 
34036                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34037                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34038                 
34039                 if(b.size == 'md-left' || b.size == 'md-right'){
34040                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34041                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34042                 }
34043                 
34044                 b.el.setWidth(width);
34045                 b.el.setHeight(height);
34046                 // iframe?
34047                 b.el.select('iframe',true).setSize(width,height);
34048                 
34049             }, this);
34050             
34051             for (var i = 0; i < this.cols; i++){
34052                 
34053                 if(maxY[i] < maxY[col]){
34054                     col = i;
34055                     continue;
34056                 }
34057                 
34058                 col = Math.min(col, i);
34059                 
34060             }
34061             
34062             x = pos.x + col * (this.colWidth + this.padWidth);
34063             
34064             y = maxY[col];
34065             
34066             var positions = [];
34067             
34068             switch (box.length){
34069                 case 1 :
34070                     positions = this.getVerticalOneBoxColPositions(x, y, box);
34071                     break;
34072                 case 2 :
34073                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
34074                     break;
34075                 case 3 :
34076                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
34077                     break;
34078                 case 4 :
34079                     positions = this.getVerticalFourBoxColPositions(x, y, box);
34080                     break;
34081                 default :
34082                     break;
34083             }
34084             
34085             Roo.each(box, function(b,kk){
34086                 
34087                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34088                 
34089                 var sz = b.el.getSize();
34090                 
34091                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
34092                 
34093             }, this);
34094             
34095         }, this);
34096         
34097         var mY = 0;
34098         
34099         for (var i = 0; i < this.cols; i++){
34100             mY = Math.max(mY, maxY[i]);
34101         }
34102         
34103         this.el.setHeight(mY - pos.y);
34104         
34105     },
34106     
34107 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
34108 //    {
34109 //        var pos = this.el.getBox(true);
34110 //        var x = pos.x;
34111 //        var y = pos.y;
34112 //        var maxX = pos.right;
34113 //        
34114 //        var maxHeight = 0;
34115 //        
34116 //        Roo.each(items, function(item, k){
34117 //            
34118 //            var c = k % 2;
34119 //            
34120 //            item.el.position('absolute');
34121 //                
34122 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
34123 //
34124 //            item.el.setWidth(width);
34125 //
34126 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
34127 //
34128 //            item.el.setHeight(height);
34129 //            
34130 //            if(c == 0){
34131 //                item.el.setXY([x, y], isInstant ? false : true);
34132 //            } else {
34133 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
34134 //            }
34135 //            
34136 //            y = y + height + this.alternativePadWidth;
34137 //            
34138 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34139 //            
34140 //        }, this);
34141 //        
34142 //        this.el.setHeight(maxHeight);
34143 //        
34144 //    },
34145     
34146     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34147     {
34148         var pos = this.el.getBox(true);
34149         
34150         var minX = pos.x;
34151         var minY = pos.y;
34152         
34153         var maxX = pos.right;
34154         
34155         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34156         
34157         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34158         
34159         Roo.each(queue, function(box, k){
34160             
34161             Roo.each(box, function(b, kk){
34162                 
34163                 b.el.position('absolute');
34164                 
34165                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34166                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34167                 
34168                 if(b.size == 'md-left' || b.size == 'md-right'){
34169                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34170                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34171                 }
34172                 
34173                 b.el.setWidth(width);
34174                 b.el.setHeight(height);
34175                 
34176             }, this);
34177             
34178             if(!box.length){
34179                 return;
34180             }
34181             
34182             var positions = [];
34183             
34184             switch (box.length){
34185                 case 1 :
34186                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34187                     break;
34188                 case 2 :
34189                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34190                     break;
34191                 case 3 :
34192                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34193                     break;
34194                 case 4 :
34195                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34196                     break;
34197                 default :
34198                     break;
34199             }
34200             
34201             Roo.each(box, function(b,kk){
34202                 
34203                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34204                 
34205                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34206                 
34207             }, this);
34208             
34209         }, this);
34210         
34211     },
34212     
34213     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34214     {
34215         Roo.each(eItems, function(b,k){
34216             
34217             b.size = (k == 0) ? 'sm' : 'xs';
34218             b.x = (k == 0) ? 2 : 1;
34219             b.y = (k == 0) ? 2 : 1;
34220             
34221             b.el.position('absolute');
34222             
34223             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34224                 
34225             b.el.setWidth(width);
34226             
34227             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34228             
34229             b.el.setHeight(height);
34230             
34231         }, this);
34232
34233         var positions = [];
34234         
34235         positions.push({
34236             x : maxX - this.unitWidth * 2 - this.gutter,
34237             y : minY
34238         });
34239         
34240         positions.push({
34241             x : maxX - this.unitWidth,
34242             y : minY + (this.unitWidth + this.gutter) * 2
34243         });
34244         
34245         positions.push({
34246             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34247             y : minY
34248         });
34249         
34250         Roo.each(eItems, function(b,k){
34251             
34252             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34253
34254         }, this);
34255         
34256     },
34257     
34258     getVerticalOneBoxColPositions : function(x, y, box)
34259     {
34260         var pos = [];
34261         
34262         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34263         
34264         if(box[0].size == 'md-left'){
34265             rand = 0;
34266         }
34267         
34268         if(box[0].size == 'md-right'){
34269             rand = 1;
34270         }
34271         
34272         pos.push({
34273             x : x + (this.unitWidth + this.gutter) * rand,
34274             y : y
34275         });
34276         
34277         return pos;
34278     },
34279     
34280     getVerticalTwoBoxColPositions : function(x, y, box)
34281     {
34282         var pos = [];
34283         
34284         if(box[0].size == 'xs'){
34285             
34286             pos.push({
34287                 x : x,
34288                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34289             });
34290
34291             pos.push({
34292                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34293                 y : y
34294             });
34295             
34296             return pos;
34297             
34298         }
34299         
34300         pos.push({
34301             x : x,
34302             y : y
34303         });
34304
34305         pos.push({
34306             x : x + (this.unitWidth + this.gutter) * 2,
34307             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34308         });
34309         
34310         return pos;
34311         
34312     },
34313     
34314     getVerticalThreeBoxColPositions : function(x, y, box)
34315     {
34316         var pos = [];
34317         
34318         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34319             
34320             pos.push({
34321                 x : x,
34322                 y : y
34323             });
34324
34325             pos.push({
34326                 x : x + (this.unitWidth + this.gutter) * 1,
34327                 y : y
34328             });
34329             
34330             pos.push({
34331                 x : x + (this.unitWidth + this.gutter) * 2,
34332                 y : y
34333             });
34334             
34335             return pos;
34336             
34337         }
34338         
34339         if(box[0].size == 'xs' && box[1].size == 'xs'){
34340             
34341             pos.push({
34342                 x : x,
34343                 y : y
34344             });
34345
34346             pos.push({
34347                 x : x,
34348                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34349             });
34350             
34351             pos.push({
34352                 x : x + (this.unitWidth + this.gutter) * 1,
34353                 y : y
34354             });
34355             
34356             return pos;
34357             
34358         }
34359         
34360         pos.push({
34361             x : x,
34362             y : y
34363         });
34364
34365         pos.push({
34366             x : x + (this.unitWidth + this.gutter) * 2,
34367             y : y
34368         });
34369
34370         pos.push({
34371             x : x + (this.unitWidth + this.gutter) * 2,
34372             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34373         });
34374             
34375         return pos;
34376         
34377     },
34378     
34379     getVerticalFourBoxColPositions : function(x, y, box)
34380     {
34381         var pos = [];
34382         
34383         if(box[0].size == 'xs'){
34384             
34385             pos.push({
34386                 x : x,
34387                 y : y
34388             });
34389
34390             pos.push({
34391                 x : x,
34392                 y : y + (this.unitHeight + this.gutter) * 1
34393             });
34394             
34395             pos.push({
34396                 x : x,
34397                 y : y + (this.unitHeight + this.gutter) * 2
34398             });
34399             
34400             pos.push({
34401                 x : x + (this.unitWidth + this.gutter) * 1,
34402                 y : y
34403             });
34404             
34405             return pos;
34406             
34407         }
34408         
34409         pos.push({
34410             x : x,
34411             y : y
34412         });
34413
34414         pos.push({
34415             x : x + (this.unitWidth + this.gutter) * 2,
34416             y : y
34417         });
34418
34419         pos.push({
34420             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34421             y : y + (this.unitHeight + this.gutter) * 1
34422         });
34423
34424         pos.push({
34425             x : x + (this.unitWidth + this.gutter) * 2,
34426             y : y + (this.unitWidth + this.gutter) * 2
34427         });
34428
34429         return pos;
34430         
34431     },
34432     
34433     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34434     {
34435         var pos = [];
34436         
34437         if(box[0].size == 'md-left'){
34438             pos.push({
34439                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34440                 y : minY
34441             });
34442             
34443             return pos;
34444         }
34445         
34446         if(box[0].size == 'md-right'){
34447             pos.push({
34448                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34449                 y : minY + (this.unitWidth + this.gutter) * 1
34450             });
34451             
34452             return pos;
34453         }
34454         
34455         var rand = Math.floor(Math.random() * (4 - box[0].y));
34456         
34457         pos.push({
34458             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34459             y : minY + (this.unitWidth + this.gutter) * rand
34460         });
34461         
34462         return pos;
34463         
34464     },
34465     
34466     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34467     {
34468         var pos = [];
34469         
34470         if(box[0].size == 'xs'){
34471             
34472             pos.push({
34473                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34474                 y : minY
34475             });
34476
34477             pos.push({
34478                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34479                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34480             });
34481             
34482             return pos;
34483             
34484         }
34485         
34486         pos.push({
34487             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34488             y : minY
34489         });
34490
34491         pos.push({
34492             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34493             y : minY + (this.unitWidth + this.gutter) * 2
34494         });
34495         
34496         return pos;
34497         
34498     },
34499     
34500     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34501     {
34502         var pos = [];
34503         
34504         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34505             
34506             pos.push({
34507                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34508                 y : minY
34509             });
34510
34511             pos.push({
34512                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34513                 y : minY + (this.unitWidth + this.gutter) * 1
34514             });
34515             
34516             pos.push({
34517                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34518                 y : minY + (this.unitWidth + this.gutter) * 2
34519             });
34520             
34521             return pos;
34522             
34523         }
34524         
34525         if(box[0].size == 'xs' && box[1].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[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34534                 y : minY
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) * 1
34540             });
34541             
34542             return pos;
34543             
34544         }
34545         
34546         pos.push({
34547             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34548             y : minY
34549         });
34550
34551         pos.push({
34552             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34553             y : minY + (this.unitWidth + this.gutter) * 2
34554         });
34555
34556         pos.push({
34557             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34558             y : minY + (this.unitWidth + this.gutter) * 2
34559         });
34560             
34561         return pos;
34562         
34563     },
34564     
34565     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34566     {
34567         var pos = [];
34568         
34569         if(box[0].size == 'xs'){
34570             
34571             pos.push({
34572                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34573                 y : minY
34574             });
34575
34576             pos.push({
34577                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34578                 y : minY
34579             });
34580             
34581             pos.push({
34582                 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),
34583                 y : minY
34584             });
34585             
34586             pos.push({
34587                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34588                 y : minY + (this.unitWidth + this.gutter) * 1
34589             });
34590             
34591             return pos;
34592             
34593         }
34594         
34595         pos.push({
34596             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34597             y : minY
34598         });
34599         
34600         pos.push({
34601             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34602             y : minY + (this.unitWidth + this.gutter) * 2
34603         });
34604         
34605         pos.push({
34606             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34607             y : minY + (this.unitWidth + this.gutter) * 2
34608         });
34609         
34610         pos.push({
34611             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),
34612             y : minY + (this.unitWidth + this.gutter) * 2
34613         });
34614
34615         return pos;
34616         
34617     },
34618     
34619     /**
34620     * remove a Masonry Brick
34621     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34622     */
34623     removeBrick : function(brick_id)
34624     {
34625         if (!brick_id) {
34626             return;
34627         }
34628         
34629         for (var i = 0; i<this.bricks.length; i++) {
34630             if (this.bricks[i].id == brick_id) {
34631                 this.bricks.splice(i,1);
34632                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34633                 this.initial();
34634             }
34635         }
34636     },
34637     
34638     /**
34639     * adds a Masonry Brick
34640     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34641     */
34642     addBrick : function(cfg)
34643     {
34644         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34645         //this.register(cn);
34646         cn.parentId = this.id;
34647         cn.render(this.el);
34648         return cn;
34649     },
34650     
34651     /**
34652     * register a Masonry Brick
34653     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34654     */
34655     
34656     register : function(brick)
34657     {
34658         this.bricks.push(brick);
34659         brick.masonryId = this.id;
34660     },
34661     
34662     /**
34663     * clear all the Masonry Brick
34664     */
34665     clearAll : function()
34666     {
34667         this.bricks = [];
34668         //this.getChildContainer().dom.innerHTML = "";
34669         this.el.dom.innerHTML = '';
34670     },
34671     
34672     getSelected : function()
34673     {
34674         if (!this.selectedBrick) {
34675             return false;
34676         }
34677         
34678         return this.selectedBrick;
34679     }
34680 });
34681
34682 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34683     
34684     groups: {},
34685      /**
34686     * register a Masonry Layout
34687     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34688     */
34689     
34690     register : function(layout)
34691     {
34692         this.groups[layout.id] = layout;
34693     },
34694     /**
34695     * fetch a  Masonry Layout based on the masonry layout ID
34696     * @param {string} the masonry layout to add
34697     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34698     */
34699     
34700     get: function(layout_id) {
34701         if (typeof(this.groups[layout_id]) == 'undefined') {
34702             return false;
34703         }
34704         return this.groups[layout_id] ;
34705     }
34706     
34707     
34708     
34709 });
34710
34711  
34712
34713  /**
34714  *
34715  * This is based on 
34716  * http://masonry.desandro.com
34717  *
34718  * The idea is to render all the bricks based on vertical width...
34719  *
34720  * The original code extends 'outlayer' - we might need to use that....
34721  * 
34722  */
34723
34724
34725 /**
34726  * @class Roo.bootstrap.LayoutMasonryAuto
34727  * @extends Roo.bootstrap.Component
34728  * Bootstrap Layout Masonry class
34729  * 
34730  * @constructor
34731  * Create a new Element
34732  * @param {Object} config The config object
34733  */
34734
34735 Roo.bootstrap.LayoutMasonryAuto = function(config){
34736     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34737 };
34738
34739 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34740     
34741       /**
34742      * @cfg {Boolean} isFitWidth  - resize the width..
34743      */   
34744     isFitWidth : false,  // options..
34745     /**
34746      * @cfg {Boolean} isOriginLeft = left align?
34747      */   
34748     isOriginLeft : true,
34749     /**
34750      * @cfg {Boolean} isOriginTop = top align?
34751      */   
34752     isOriginTop : false,
34753     /**
34754      * @cfg {Boolean} isLayoutInstant = no animation?
34755      */   
34756     isLayoutInstant : false, // needed?
34757     /**
34758      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34759      */   
34760     isResizingContainer : true,
34761     /**
34762      * @cfg {Number} columnWidth  width of the columns 
34763      */   
34764     
34765     columnWidth : 0,
34766     
34767     /**
34768      * @cfg {Number} maxCols maximum number of columns
34769      */   
34770     
34771     maxCols: 0,
34772     /**
34773      * @cfg {Number} padHeight padding below box..
34774      */   
34775     
34776     padHeight : 10, 
34777     
34778     /**
34779      * @cfg {Boolean} isAutoInitial defalut true
34780      */   
34781     
34782     isAutoInitial : true, 
34783     
34784     // private?
34785     gutter : 0,
34786     
34787     containerWidth: 0,
34788     initialColumnWidth : 0,
34789     currentSize : null,
34790     
34791     colYs : null, // array.
34792     maxY : 0,
34793     padWidth: 10,
34794     
34795     
34796     tag: 'div',
34797     cls: '',
34798     bricks: null, //CompositeElement
34799     cols : 0, // array?
34800     // element : null, // wrapped now this.el
34801     _isLayoutInited : null, 
34802     
34803     
34804     getAutoCreate : function(){
34805         
34806         var cfg = {
34807             tag: this.tag,
34808             cls: 'blog-masonary-wrapper ' + this.cls,
34809             cn : {
34810                 cls : 'mas-boxes masonary'
34811             }
34812         };
34813         
34814         return cfg;
34815     },
34816     
34817     getChildContainer: function( )
34818     {
34819         if (this.boxesEl) {
34820             return this.boxesEl;
34821         }
34822         
34823         this.boxesEl = this.el.select('.mas-boxes').first();
34824         
34825         return this.boxesEl;
34826     },
34827     
34828     
34829     initEvents : function()
34830     {
34831         var _this = this;
34832         
34833         if(this.isAutoInitial){
34834             Roo.log('hook children rendered');
34835             this.on('childrenrendered', function() {
34836                 Roo.log('children rendered');
34837                 _this.initial();
34838             } ,this);
34839         }
34840         
34841     },
34842     
34843     initial : function()
34844     {
34845         this.reloadItems();
34846
34847         this.currentSize = this.el.getBox(true);
34848
34849         /// was window resize... - let's see if this works..
34850         Roo.EventManager.onWindowResize(this.resize, this); 
34851
34852         if(!this.isAutoInitial){
34853             this.layout();
34854             return;
34855         }
34856         
34857         this.layout.defer(500,this);
34858     },
34859     
34860     reloadItems: function()
34861     {
34862         this.bricks = this.el.select('.masonry-brick', true);
34863         
34864         this.bricks.each(function(b) {
34865             //Roo.log(b.getSize());
34866             if (!b.attr('originalwidth')) {
34867                 b.attr('originalwidth',  b.getSize().width);
34868             }
34869             
34870         });
34871         
34872         Roo.log(this.bricks.elements.length);
34873     },
34874     
34875     resize : function()
34876     {
34877         Roo.log('resize');
34878         var cs = this.el.getBox(true);
34879         
34880         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34881             Roo.log("no change in with or X");
34882             return;
34883         }
34884         this.currentSize = cs;
34885         this.layout();
34886     },
34887     
34888     layout : function()
34889     {
34890          Roo.log('layout');
34891         this._resetLayout();
34892         //this._manageStamps();
34893       
34894         // don't animate first layout
34895         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34896         this.layoutItems( isInstant );
34897       
34898         // flag for initalized
34899         this._isLayoutInited = true;
34900     },
34901     
34902     layoutItems : function( isInstant )
34903     {
34904         //var items = this._getItemsForLayout( this.items );
34905         // original code supports filtering layout items.. we just ignore it..
34906         
34907         this._layoutItems( this.bricks , isInstant );
34908       
34909         this._postLayout();
34910     },
34911     _layoutItems : function ( items , isInstant)
34912     {
34913        //this.fireEvent( 'layout', this, items );
34914     
34915
34916         if ( !items || !items.elements.length ) {
34917           // no items, emit event with empty array
34918             return;
34919         }
34920
34921         var queue = [];
34922         items.each(function(item) {
34923             Roo.log("layout item");
34924             Roo.log(item);
34925             // get x/y object from method
34926             var position = this._getItemLayoutPosition( item );
34927             // enqueue
34928             position.item = item;
34929             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34930             queue.push( position );
34931         }, this);
34932       
34933         this._processLayoutQueue( queue );
34934     },
34935     /** Sets position of item in DOM
34936     * @param {Element} item
34937     * @param {Number} x - horizontal position
34938     * @param {Number} y - vertical position
34939     * @param {Boolean} isInstant - disables transitions
34940     */
34941     _processLayoutQueue : function( queue )
34942     {
34943         for ( var i=0, len = queue.length; i < len; i++ ) {
34944             var obj = queue[i];
34945             obj.item.position('absolute');
34946             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34947         }
34948     },
34949       
34950     
34951     /**
34952     * Any logic you want to do after each layout,
34953     * i.e. size the container
34954     */
34955     _postLayout : function()
34956     {
34957         this.resizeContainer();
34958     },
34959     
34960     resizeContainer : function()
34961     {
34962         if ( !this.isResizingContainer ) {
34963             return;
34964         }
34965         var size = this._getContainerSize();
34966         if ( size ) {
34967             this.el.setSize(size.width,size.height);
34968             this.boxesEl.setSize(size.width,size.height);
34969         }
34970     },
34971     
34972     
34973     
34974     _resetLayout : function()
34975     {
34976         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34977         this.colWidth = this.el.getWidth();
34978         //this.gutter = this.el.getWidth(); 
34979         
34980         this.measureColumns();
34981
34982         // reset column Y
34983         var i = this.cols;
34984         this.colYs = [];
34985         while (i--) {
34986             this.colYs.push( 0 );
34987         }
34988     
34989         this.maxY = 0;
34990     },
34991
34992     measureColumns : function()
34993     {
34994         this.getContainerWidth();
34995       // if columnWidth is 0, default to outerWidth of first item
34996         if ( !this.columnWidth ) {
34997             var firstItem = this.bricks.first();
34998             Roo.log(firstItem);
34999             this.columnWidth  = this.containerWidth;
35000             if (firstItem && firstItem.attr('originalwidth') ) {
35001                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
35002             }
35003             // columnWidth fall back to item of first element
35004             Roo.log("set column width?");
35005                         this.initialColumnWidth = this.columnWidth  ;
35006
35007             // if first elem has no width, default to size of container
35008             
35009         }
35010         
35011         
35012         if (this.initialColumnWidth) {
35013             this.columnWidth = this.initialColumnWidth;
35014         }
35015         
35016         
35017             
35018         // column width is fixed at the top - however if container width get's smaller we should
35019         // reduce it...
35020         
35021         // this bit calcs how man columns..
35022             
35023         var columnWidth = this.columnWidth += this.gutter;
35024       
35025         // calculate columns
35026         var containerWidth = this.containerWidth + this.gutter;
35027         
35028         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
35029         // fix rounding errors, typically with gutters
35030         var excess = columnWidth - containerWidth % columnWidth;
35031         
35032         
35033         // if overshoot is less than a pixel, round up, otherwise floor it
35034         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
35035         cols = Math[ mathMethod ]( cols );
35036         this.cols = Math.max( cols, 1 );
35037         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
35038         
35039          // padding positioning..
35040         var totalColWidth = this.cols * this.columnWidth;
35041         var padavail = this.containerWidth - totalColWidth;
35042         // so for 2 columns - we need 3 'pads'
35043         
35044         var padNeeded = (1+this.cols) * this.padWidth;
35045         
35046         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
35047         
35048         this.columnWidth += padExtra
35049         //this.padWidth = Math.floor(padavail /  ( this.cols));
35050         
35051         // adjust colum width so that padding is fixed??
35052         
35053         // we have 3 columns ... total = width * 3
35054         // we have X left over... that should be used by 
35055         
35056         //if (this.expandC) {
35057             
35058         //}
35059         
35060         
35061         
35062     },
35063     
35064     getContainerWidth : function()
35065     {
35066        /* // container is parent if fit width
35067         var container = this.isFitWidth ? this.element.parentNode : this.element;
35068         // check that this.size and size are there
35069         // IE8 triggers resize on body size change, so they might not be
35070         
35071         var size = getSize( container );  //FIXME
35072         this.containerWidth = size && size.innerWidth; //FIXME
35073         */
35074          
35075         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
35076         
35077     },
35078     
35079     _getItemLayoutPosition : function( item )  // what is item?
35080     {
35081         // we resize the item to our columnWidth..
35082       
35083         item.setWidth(this.columnWidth);
35084         item.autoBoxAdjust  = false;
35085         
35086         var sz = item.getSize();
35087  
35088         // how many columns does this brick span
35089         var remainder = this.containerWidth % this.columnWidth;
35090         
35091         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
35092         // round if off by 1 pixel, otherwise use ceil
35093         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
35094         colSpan = Math.min( colSpan, this.cols );
35095         
35096         // normally this should be '1' as we dont' currently allow multi width columns..
35097         
35098         var colGroup = this._getColGroup( colSpan );
35099         // get the minimum Y value from the columns
35100         var minimumY = Math.min.apply( Math, colGroup );
35101         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35102         
35103         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
35104          
35105         // position the brick
35106         var position = {
35107             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
35108             y: this.currentSize.y + minimumY + this.padHeight
35109         };
35110         
35111         Roo.log(position);
35112         // apply setHeight to necessary columns
35113         var setHeight = minimumY + sz.height + this.padHeight;
35114         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
35115         
35116         var setSpan = this.cols + 1 - colGroup.length;
35117         for ( var i = 0; i < setSpan; i++ ) {
35118           this.colYs[ shortColIndex + i ] = setHeight ;
35119         }
35120       
35121         return position;
35122     },
35123     
35124     /**
35125      * @param {Number} colSpan - number of columns the element spans
35126      * @returns {Array} colGroup
35127      */
35128     _getColGroup : function( colSpan )
35129     {
35130         if ( colSpan < 2 ) {
35131           // if brick spans only one column, use all the column Ys
35132           return this.colYs;
35133         }
35134       
35135         var colGroup = [];
35136         // how many different places could this brick fit horizontally
35137         var groupCount = this.cols + 1 - colSpan;
35138         // for each group potential horizontal position
35139         for ( var i = 0; i < groupCount; i++ ) {
35140           // make an array of colY values for that one group
35141           var groupColYs = this.colYs.slice( i, i + colSpan );
35142           // and get the max value of the array
35143           colGroup[i] = Math.max.apply( Math, groupColYs );
35144         }
35145         return colGroup;
35146     },
35147     /*
35148     _manageStamp : function( stamp )
35149     {
35150         var stampSize =  stamp.getSize();
35151         var offset = stamp.getBox();
35152         // get the columns that this stamp affects
35153         var firstX = this.isOriginLeft ? offset.x : offset.right;
35154         var lastX = firstX + stampSize.width;
35155         var firstCol = Math.floor( firstX / this.columnWidth );
35156         firstCol = Math.max( 0, firstCol );
35157         
35158         var lastCol = Math.floor( lastX / this.columnWidth );
35159         // lastCol should not go over if multiple of columnWidth #425
35160         lastCol -= lastX % this.columnWidth ? 0 : 1;
35161         lastCol = Math.min( this.cols - 1, lastCol );
35162         
35163         // set colYs to bottom of the stamp
35164         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35165             stampSize.height;
35166             
35167         for ( var i = firstCol; i <= lastCol; i++ ) {
35168           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35169         }
35170     },
35171     */
35172     
35173     _getContainerSize : function()
35174     {
35175         this.maxY = Math.max.apply( Math, this.colYs );
35176         var size = {
35177             height: this.maxY
35178         };
35179       
35180         if ( this.isFitWidth ) {
35181             size.width = this._getContainerFitWidth();
35182         }
35183       
35184         return size;
35185     },
35186     
35187     _getContainerFitWidth : function()
35188     {
35189         var unusedCols = 0;
35190         // count unused columns
35191         var i = this.cols;
35192         while ( --i ) {
35193           if ( this.colYs[i] !== 0 ) {
35194             break;
35195           }
35196           unusedCols++;
35197         }
35198         // fit container to columns that have been used
35199         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35200     },
35201     
35202     needsResizeLayout : function()
35203     {
35204         var previousWidth = this.containerWidth;
35205         this.getContainerWidth();
35206         return previousWidth !== this.containerWidth;
35207     }
35208  
35209 });
35210
35211  
35212
35213  /*
35214  * - LGPL
35215  *
35216  * element
35217  * 
35218  */
35219
35220 /**
35221  * @class Roo.bootstrap.MasonryBrick
35222  * @extends Roo.bootstrap.Component
35223  * Bootstrap MasonryBrick class
35224  * 
35225  * @constructor
35226  * Create a new MasonryBrick
35227  * @param {Object} config The config object
35228  */
35229
35230 Roo.bootstrap.MasonryBrick = function(config){
35231     
35232     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35233     
35234     Roo.bootstrap.MasonryBrick.register(this);
35235     
35236     this.addEvents({
35237         // raw events
35238         /**
35239          * @event click
35240          * When a MasonryBrick is clcik
35241          * @param {Roo.bootstrap.MasonryBrick} this
35242          * @param {Roo.EventObject} e
35243          */
35244         "click" : true
35245     });
35246 };
35247
35248 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35249     
35250     /**
35251      * @cfg {String} title
35252      */   
35253     title : '',
35254     /**
35255      * @cfg {String} html
35256      */   
35257     html : '',
35258     /**
35259      * @cfg {String} bgimage
35260      */   
35261     bgimage : '',
35262     /**
35263      * @cfg {String} videourl
35264      */   
35265     videourl : '',
35266     /**
35267      * @cfg {String} cls
35268      */   
35269     cls : '',
35270     /**
35271      * @cfg {String} href
35272      */   
35273     href : '',
35274     /**
35275      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35276      */   
35277     size : 'xs',
35278     
35279     /**
35280      * @cfg {String} placetitle (center|bottom)
35281      */   
35282     placetitle : '',
35283     
35284     /**
35285      * @cfg {Boolean} isFitContainer defalut true
35286      */   
35287     isFitContainer : true, 
35288     
35289     /**
35290      * @cfg {Boolean} preventDefault defalut false
35291      */   
35292     preventDefault : false, 
35293     
35294     /**
35295      * @cfg {Boolean} inverse defalut false
35296      */   
35297     maskInverse : false, 
35298     
35299     getAutoCreate : function()
35300     {
35301         if(!this.isFitContainer){
35302             return this.getSplitAutoCreate();
35303         }
35304         
35305         var cls = 'masonry-brick masonry-brick-full';
35306         
35307         if(this.href.length){
35308             cls += ' masonry-brick-link';
35309         }
35310         
35311         if(this.bgimage.length){
35312             cls += ' masonry-brick-image';
35313         }
35314         
35315         if(this.maskInverse){
35316             cls += ' mask-inverse';
35317         }
35318         
35319         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35320             cls += ' enable-mask';
35321         }
35322         
35323         if(this.size){
35324             cls += ' masonry-' + this.size + '-brick';
35325         }
35326         
35327         if(this.placetitle.length){
35328             
35329             switch (this.placetitle) {
35330                 case 'center' :
35331                     cls += ' masonry-center-title';
35332                     break;
35333                 case 'bottom' :
35334                     cls += ' masonry-bottom-title';
35335                     break;
35336                 default:
35337                     break;
35338             }
35339             
35340         } else {
35341             if(!this.html.length && !this.bgimage.length){
35342                 cls += ' masonry-center-title';
35343             }
35344
35345             if(!this.html.length && this.bgimage.length){
35346                 cls += ' masonry-bottom-title';
35347             }
35348         }
35349         
35350         if(this.cls){
35351             cls += ' ' + this.cls;
35352         }
35353         
35354         var cfg = {
35355             tag: (this.href.length) ? 'a' : 'div',
35356             cls: cls,
35357             cn: [
35358                 {
35359                     tag: 'div',
35360                     cls: 'masonry-brick-mask'
35361                 },
35362                 {
35363                     tag: 'div',
35364                     cls: 'masonry-brick-paragraph',
35365                     cn: []
35366                 }
35367             ]
35368         };
35369         
35370         if(this.href.length){
35371             cfg.href = this.href;
35372         }
35373         
35374         var cn = cfg.cn[1].cn;
35375         
35376         if(this.title.length){
35377             cn.push({
35378                 tag: 'h4',
35379                 cls: 'masonry-brick-title',
35380                 html: this.title
35381             });
35382         }
35383         
35384         if(this.html.length){
35385             cn.push({
35386                 tag: 'p',
35387                 cls: 'masonry-brick-text',
35388                 html: this.html
35389             });
35390         }
35391         
35392         if (!this.title.length && !this.html.length) {
35393             cfg.cn[1].cls += ' hide';
35394         }
35395         
35396         if(this.bgimage.length){
35397             cfg.cn.push({
35398                 tag: 'img',
35399                 cls: 'masonry-brick-image-view',
35400                 src: this.bgimage
35401             });
35402         }
35403         
35404         if(this.videourl.length){
35405             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35406             // youtube support only?
35407             cfg.cn.push({
35408                 tag: 'iframe',
35409                 cls: 'masonry-brick-image-view',
35410                 src: vurl,
35411                 frameborder : 0,
35412                 allowfullscreen : true
35413             });
35414         }
35415         
35416         return cfg;
35417         
35418     },
35419     
35420     getSplitAutoCreate : function()
35421     {
35422         var cls = 'masonry-brick masonry-brick-split';
35423         
35424         if(this.href.length){
35425             cls += ' masonry-brick-link';
35426         }
35427         
35428         if(this.bgimage.length){
35429             cls += ' masonry-brick-image';
35430         }
35431         
35432         if(this.size){
35433             cls += ' masonry-' + this.size + '-brick';
35434         }
35435         
35436         switch (this.placetitle) {
35437             case 'center' :
35438                 cls += ' masonry-center-title';
35439                 break;
35440             case 'bottom' :
35441                 cls += ' masonry-bottom-title';
35442                 break;
35443             default:
35444                 if(!this.bgimage.length){
35445                     cls += ' masonry-center-title';
35446                 }
35447
35448                 if(this.bgimage.length){
35449                     cls += ' masonry-bottom-title';
35450                 }
35451                 break;
35452         }
35453         
35454         if(this.cls){
35455             cls += ' ' + this.cls;
35456         }
35457         
35458         var cfg = {
35459             tag: (this.href.length) ? 'a' : 'div',
35460             cls: cls,
35461             cn: [
35462                 {
35463                     tag: 'div',
35464                     cls: 'masonry-brick-split-head',
35465                     cn: [
35466                         {
35467                             tag: 'div',
35468                             cls: 'masonry-brick-paragraph',
35469                             cn: []
35470                         }
35471                     ]
35472                 },
35473                 {
35474                     tag: 'div',
35475                     cls: 'masonry-brick-split-body',
35476                     cn: []
35477                 }
35478             ]
35479         };
35480         
35481         if(this.href.length){
35482             cfg.href = this.href;
35483         }
35484         
35485         if(this.title.length){
35486             cfg.cn[0].cn[0].cn.push({
35487                 tag: 'h4',
35488                 cls: 'masonry-brick-title',
35489                 html: this.title
35490             });
35491         }
35492         
35493         if(this.html.length){
35494             cfg.cn[1].cn.push({
35495                 tag: 'p',
35496                 cls: 'masonry-brick-text',
35497                 html: this.html
35498             });
35499         }
35500
35501         if(this.bgimage.length){
35502             cfg.cn[0].cn.push({
35503                 tag: 'img',
35504                 cls: 'masonry-brick-image-view',
35505                 src: this.bgimage
35506             });
35507         }
35508         
35509         if(this.videourl.length){
35510             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35511             // youtube support only?
35512             cfg.cn[0].cn.cn.push({
35513                 tag: 'iframe',
35514                 cls: 'masonry-brick-image-view',
35515                 src: vurl,
35516                 frameborder : 0,
35517                 allowfullscreen : true
35518             });
35519         }
35520         
35521         return cfg;
35522     },
35523     
35524     initEvents: function() 
35525     {
35526         switch (this.size) {
35527             case 'xs' :
35528                 this.x = 1;
35529                 this.y = 1;
35530                 break;
35531             case 'sm' :
35532                 this.x = 2;
35533                 this.y = 2;
35534                 break;
35535             case 'md' :
35536             case 'md-left' :
35537             case 'md-right' :
35538                 this.x = 3;
35539                 this.y = 3;
35540                 break;
35541             case 'tall' :
35542                 this.x = 2;
35543                 this.y = 3;
35544                 break;
35545             case 'wide' :
35546                 this.x = 3;
35547                 this.y = 2;
35548                 break;
35549             case 'wide-thin' :
35550                 this.x = 3;
35551                 this.y = 1;
35552                 break;
35553                         
35554             default :
35555                 break;
35556         }
35557         
35558         if(Roo.isTouch){
35559             this.el.on('touchstart', this.onTouchStart, this);
35560             this.el.on('touchmove', this.onTouchMove, this);
35561             this.el.on('touchend', this.onTouchEnd, this);
35562             this.el.on('contextmenu', this.onContextMenu, this);
35563         } else {
35564             this.el.on('mouseenter'  ,this.enter, this);
35565             this.el.on('mouseleave', this.leave, this);
35566             this.el.on('click', this.onClick, this);
35567         }
35568         
35569         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35570             this.parent().bricks.push(this);   
35571         }
35572         
35573     },
35574     
35575     onClick: function(e, el)
35576     {
35577         var time = this.endTimer - this.startTimer;
35578         // Roo.log(e.preventDefault());
35579         if(Roo.isTouch){
35580             if(time > 1000){
35581                 e.preventDefault();
35582                 return;
35583             }
35584         }
35585         
35586         if(!this.preventDefault){
35587             return;
35588         }
35589         
35590         e.preventDefault();
35591         
35592         if (this.activeClass != '') {
35593             this.selectBrick();
35594         }
35595         
35596         this.fireEvent('click', this, e);
35597     },
35598     
35599     enter: function(e, el)
35600     {
35601         e.preventDefault();
35602         
35603         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35604             return;
35605         }
35606         
35607         if(this.bgimage.length && this.html.length){
35608             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35609         }
35610     },
35611     
35612     leave: function(e, el)
35613     {
35614         e.preventDefault();
35615         
35616         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35617             return;
35618         }
35619         
35620         if(this.bgimage.length && this.html.length){
35621             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35622         }
35623     },
35624     
35625     onTouchStart: function(e, el)
35626     {
35627 //        e.preventDefault();
35628         
35629         this.touchmoved = false;
35630         
35631         if(!this.isFitContainer){
35632             return;
35633         }
35634         
35635         if(!this.bgimage.length || !this.html.length){
35636             return;
35637         }
35638         
35639         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35640         
35641         this.timer = new Date().getTime();
35642         
35643     },
35644     
35645     onTouchMove: function(e, el)
35646     {
35647         this.touchmoved = true;
35648     },
35649     
35650     onContextMenu : function(e,el)
35651     {
35652         e.preventDefault();
35653         e.stopPropagation();
35654         return false;
35655     },
35656     
35657     onTouchEnd: function(e, el)
35658     {
35659 //        e.preventDefault();
35660         
35661         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35662         
35663             this.leave(e,el);
35664             
35665             return;
35666         }
35667         
35668         if(!this.bgimage.length || !this.html.length){
35669             
35670             if(this.href.length){
35671                 window.location.href = this.href;
35672             }
35673             
35674             return;
35675         }
35676         
35677         if(!this.isFitContainer){
35678             return;
35679         }
35680         
35681         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35682         
35683         window.location.href = this.href;
35684     },
35685     
35686     //selection on single brick only
35687     selectBrick : function() {
35688         
35689         if (!this.parentId) {
35690             return;
35691         }
35692         
35693         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35694         var index = m.selectedBrick.indexOf(this.id);
35695         
35696         if ( index > -1) {
35697             m.selectedBrick.splice(index,1);
35698             this.el.removeClass(this.activeClass);
35699             return;
35700         }
35701         
35702         for(var i = 0; i < m.selectedBrick.length; i++) {
35703             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35704             b.el.removeClass(b.activeClass);
35705         }
35706         
35707         m.selectedBrick = [];
35708         
35709         m.selectedBrick.push(this.id);
35710         this.el.addClass(this.activeClass);
35711         return;
35712     },
35713     
35714     isSelected : function(){
35715         return this.el.hasClass(this.activeClass);
35716         
35717     }
35718 });
35719
35720 Roo.apply(Roo.bootstrap.MasonryBrick, {
35721     
35722     //groups: {},
35723     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35724      /**
35725     * register a Masonry Brick
35726     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35727     */
35728     
35729     register : function(brick)
35730     {
35731         //this.groups[brick.id] = brick;
35732         this.groups.add(brick.id, brick);
35733     },
35734     /**
35735     * fetch a  masonry brick based on the masonry brick ID
35736     * @param {string} the masonry brick to add
35737     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35738     */
35739     
35740     get: function(brick_id) 
35741     {
35742         // if (typeof(this.groups[brick_id]) == 'undefined') {
35743         //     return false;
35744         // }
35745         // return this.groups[brick_id] ;
35746         
35747         if(this.groups.key(brick_id)) {
35748             return this.groups.key(brick_id);
35749         }
35750         
35751         return false;
35752     }
35753     
35754     
35755     
35756 });
35757
35758  /*
35759  * - LGPL
35760  *
35761  * element
35762  * 
35763  */
35764
35765 /**
35766  * @class Roo.bootstrap.Brick
35767  * @extends Roo.bootstrap.Component
35768  * Bootstrap Brick class
35769  * 
35770  * @constructor
35771  * Create a new Brick
35772  * @param {Object} config The config object
35773  */
35774
35775 Roo.bootstrap.Brick = function(config){
35776     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35777     
35778     this.addEvents({
35779         // raw events
35780         /**
35781          * @event click
35782          * When a Brick is click
35783          * @param {Roo.bootstrap.Brick} this
35784          * @param {Roo.EventObject} e
35785          */
35786         "click" : true
35787     });
35788 };
35789
35790 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35791     
35792     /**
35793      * @cfg {String} title
35794      */   
35795     title : '',
35796     /**
35797      * @cfg {String} html
35798      */   
35799     html : '',
35800     /**
35801      * @cfg {String} bgimage
35802      */   
35803     bgimage : '',
35804     /**
35805      * @cfg {String} cls
35806      */   
35807     cls : '',
35808     /**
35809      * @cfg {String} href
35810      */   
35811     href : '',
35812     /**
35813      * @cfg {String} video
35814      */   
35815     video : '',
35816     /**
35817      * @cfg {Boolean} square
35818      */   
35819     square : true,
35820     
35821     getAutoCreate : function()
35822     {
35823         var cls = 'roo-brick';
35824         
35825         if(this.href.length){
35826             cls += ' roo-brick-link';
35827         }
35828         
35829         if(this.bgimage.length){
35830             cls += ' roo-brick-image';
35831         }
35832         
35833         if(!this.html.length && !this.bgimage.length){
35834             cls += ' roo-brick-center-title';
35835         }
35836         
35837         if(!this.html.length && this.bgimage.length){
35838             cls += ' roo-brick-bottom-title';
35839         }
35840         
35841         if(this.cls){
35842             cls += ' ' + this.cls;
35843         }
35844         
35845         var cfg = {
35846             tag: (this.href.length) ? 'a' : 'div',
35847             cls: cls,
35848             cn: [
35849                 {
35850                     tag: 'div',
35851                     cls: 'roo-brick-paragraph',
35852                     cn: []
35853                 }
35854             ]
35855         };
35856         
35857         if(this.href.length){
35858             cfg.href = this.href;
35859         }
35860         
35861         var cn = cfg.cn[0].cn;
35862         
35863         if(this.title.length){
35864             cn.push({
35865                 tag: 'h4',
35866                 cls: 'roo-brick-title',
35867                 html: this.title
35868             });
35869         }
35870         
35871         if(this.html.length){
35872             cn.push({
35873                 tag: 'p',
35874                 cls: 'roo-brick-text',
35875                 html: this.html
35876             });
35877         } else {
35878             cn.cls += ' hide';
35879         }
35880         
35881         if(this.bgimage.length){
35882             cfg.cn.push({
35883                 tag: 'img',
35884                 cls: 'roo-brick-image-view',
35885                 src: this.bgimage
35886             });
35887         }
35888         
35889         return cfg;
35890     },
35891     
35892     initEvents: function() 
35893     {
35894         if(this.title.length || this.html.length){
35895             this.el.on('mouseenter'  ,this.enter, this);
35896             this.el.on('mouseleave', this.leave, this);
35897         }
35898         
35899         Roo.EventManager.onWindowResize(this.resize, this); 
35900         
35901         if(this.bgimage.length){
35902             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35903             this.imageEl.on('load', this.onImageLoad, this);
35904             return;
35905         }
35906         
35907         this.resize();
35908     },
35909     
35910     onImageLoad : function()
35911     {
35912         this.resize();
35913     },
35914     
35915     resize : function()
35916     {
35917         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35918         
35919         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35920         
35921         if(this.bgimage.length){
35922             var image = this.el.select('.roo-brick-image-view', true).first();
35923             
35924             image.setWidth(paragraph.getWidth());
35925             
35926             if(this.square){
35927                 image.setHeight(paragraph.getWidth());
35928             }
35929             
35930             this.el.setHeight(image.getHeight());
35931             paragraph.setHeight(image.getHeight());
35932             
35933         }
35934         
35935     },
35936     
35937     enter: function(e, el)
35938     {
35939         e.preventDefault();
35940         
35941         if(this.bgimage.length){
35942             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35943             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35944         }
35945     },
35946     
35947     leave: function(e, el)
35948     {
35949         e.preventDefault();
35950         
35951         if(this.bgimage.length){
35952             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35953             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35954         }
35955     }
35956     
35957 });
35958
35959  
35960
35961  /*
35962  * - LGPL
35963  *
35964  * Number field 
35965  */
35966
35967 /**
35968  * @class Roo.bootstrap.NumberField
35969  * @extends Roo.bootstrap.Input
35970  * Bootstrap NumberField class
35971  * 
35972  * 
35973  * 
35974  * 
35975  * @constructor
35976  * Create a new NumberField
35977  * @param {Object} config The config object
35978  */
35979
35980 Roo.bootstrap.NumberField = function(config){
35981     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35982 };
35983
35984 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35985     
35986     /**
35987      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35988      */
35989     allowDecimals : true,
35990     /**
35991      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35992      */
35993     decimalSeparator : ".",
35994     /**
35995      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35996      */
35997     decimalPrecision : 2,
35998     /**
35999      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
36000      */
36001     allowNegative : true,
36002     
36003     /**
36004      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
36005      */
36006     allowZero: true,
36007     /**
36008      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
36009      */
36010     minValue : Number.NEGATIVE_INFINITY,
36011     /**
36012      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
36013      */
36014     maxValue : Number.MAX_VALUE,
36015     /**
36016      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
36017      */
36018     minText : "The minimum value for this field is {0}",
36019     /**
36020      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
36021      */
36022     maxText : "The maximum value for this field is {0}",
36023     /**
36024      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
36025      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
36026      */
36027     nanText : "{0} is not a valid number",
36028     /**
36029      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
36030      */
36031     thousandsDelimiter : false,
36032     /**
36033      * @cfg {String} valueAlign alignment of value
36034      */
36035     valueAlign : "left",
36036
36037     getAutoCreate : function()
36038     {
36039         var hiddenInput = {
36040             tag: 'input',
36041             type: 'hidden',
36042             id: Roo.id(),
36043             cls: 'hidden-number-input'
36044         };
36045         
36046         if (this.name) {
36047             hiddenInput.name = this.name;
36048         }
36049         
36050         this.name = '';
36051         
36052         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
36053         
36054         this.name = hiddenInput.name;
36055         
36056         if(cfg.cn.length > 0) {
36057             cfg.cn.push(hiddenInput);
36058         }
36059         
36060         return cfg;
36061     },
36062
36063     // private
36064     initEvents : function()
36065     {   
36066         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
36067         
36068         var allowed = "0123456789";
36069         
36070         if(this.allowDecimals){
36071             allowed += this.decimalSeparator;
36072         }
36073         
36074         if(this.allowNegative){
36075             allowed += "-";
36076         }
36077         
36078         if(this.thousandsDelimiter) {
36079             allowed += ",";
36080         }
36081         
36082         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
36083         
36084         var keyPress = function(e){
36085             
36086             var k = e.getKey();
36087             
36088             var c = e.getCharCode();
36089             
36090             if(
36091                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
36092                     allowed.indexOf(String.fromCharCode(c)) === -1
36093             ){
36094                 e.stopEvent();
36095                 return;
36096             }
36097             
36098             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
36099                 return;
36100             }
36101             
36102             if(allowed.indexOf(String.fromCharCode(c)) === -1){
36103                 e.stopEvent();
36104             }
36105         };
36106         
36107         this.el.on("keypress", keyPress, this);
36108     },
36109     
36110     validateValue : function(value)
36111     {
36112         
36113         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
36114             return false;
36115         }
36116         
36117         var num = this.parseValue(value);
36118         
36119         if(isNaN(num)){
36120             this.markInvalid(String.format(this.nanText, value));
36121             return false;
36122         }
36123         
36124         if(num < this.minValue){
36125             this.markInvalid(String.format(this.minText, this.minValue));
36126             return false;
36127         }
36128         
36129         if(num > this.maxValue){
36130             this.markInvalid(String.format(this.maxText, this.maxValue));
36131             return false;
36132         }
36133         
36134         return true;
36135     },
36136
36137     getValue : function()
36138     {
36139         var v = this.hiddenEl().getValue();
36140         
36141         return this.fixPrecision(this.parseValue(v));
36142     },
36143
36144     parseValue : function(value)
36145     {
36146         if(this.thousandsDelimiter) {
36147             value += "";
36148             r = new RegExp(",", "g");
36149             value = value.replace(r, "");
36150         }
36151         
36152         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36153         return isNaN(value) ? '' : value;
36154     },
36155
36156     fixPrecision : function(value)
36157     {
36158         if(this.thousandsDelimiter) {
36159             value += "";
36160             r = new RegExp(",", "g");
36161             value = value.replace(r, "");
36162         }
36163         
36164         var nan = isNaN(value);
36165         
36166         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36167             return nan ? '' : value;
36168         }
36169         return parseFloat(value).toFixed(this.decimalPrecision);
36170     },
36171
36172     setValue : function(v)
36173     {
36174         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36175         
36176         this.value = v;
36177         
36178         if(this.rendered){
36179             
36180             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36181             
36182             this.inputEl().dom.value = (v == '') ? '' :
36183                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36184             
36185             if(!this.allowZero && v === '0') {
36186                 this.hiddenEl().dom.value = '';
36187                 this.inputEl().dom.value = '';
36188             }
36189             
36190             this.validate();
36191         }
36192     },
36193
36194     decimalPrecisionFcn : function(v)
36195     {
36196         return Math.floor(v);
36197     },
36198
36199     beforeBlur : function()
36200     {
36201         var v = this.parseValue(this.getRawValue());
36202         
36203         if(v || v === 0 || v === ''){
36204             this.setValue(v);
36205         }
36206     },
36207     
36208     hiddenEl : function()
36209     {
36210         return this.el.select('input.hidden-number-input',true).first();
36211     }
36212     
36213 });
36214
36215  
36216
36217 /*
36218 * Licence: LGPL
36219 */
36220
36221 /**
36222  * @class Roo.bootstrap.DocumentSlider
36223  * @extends Roo.bootstrap.Component
36224  * Bootstrap DocumentSlider class
36225  * 
36226  * @constructor
36227  * Create a new DocumentViewer
36228  * @param {Object} config The config object
36229  */
36230
36231 Roo.bootstrap.DocumentSlider = function(config){
36232     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36233     
36234     this.files = [];
36235     
36236     this.addEvents({
36237         /**
36238          * @event initial
36239          * Fire after initEvent
36240          * @param {Roo.bootstrap.DocumentSlider} this
36241          */
36242         "initial" : true,
36243         /**
36244          * @event update
36245          * Fire after update
36246          * @param {Roo.bootstrap.DocumentSlider} this
36247          */
36248         "update" : true,
36249         /**
36250          * @event click
36251          * Fire after click
36252          * @param {Roo.bootstrap.DocumentSlider} this
36253          */
36254         "click" : true
36255     });
36256 };
36257
36258 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36259     
36260     files : false,
36261     
36262     indicator : 0,
36263     
36264     getAutoCreate : function()
36265     {
36266         var cfg = {
36267             tag : 'div',
36268             cls : 'roo-document-slider',
36269             cn : [
36270                 {
36271                     tag : 'div',
36272                     cls : 'roo-document-slider-header',
36273                     cn : [
36274                         {
36275                             tag : 'div',
36276                             cls : 'roo-document-slider-header-title'
36277                         }
36278                     ]
36279                 },
36280                 {
36281                     tag : 'div',
36282                     cls : 'roo-document-slider-body',
36283                     cn : [
36284                         {
36285                             tag : 'div',
36286                             cls : 'roo-document-slider-prev',
36287                             cn : [
36288                                 {
36289                                     tag : 'i',
36290                                     cls : 'fa fa-chevron-left'
36291                                 }
36292                             ]
36293                         },
36294                         {
36295                             tag : 'div',
36296                             cls : 'roo-document-slider-thumb',
36297                             cn : [
36298                                 {
36299                                     tag : 'img',
36300                                     cls : 'roo-document-slider-image'
36301                                 }
36302                             ]
36303                         },
36304                         {
36305                             tag : 'div',
36306                             cls : 'roo-document-slider-next',
36307                             cn : [
36308                                 {
36309                                     tag : 'i',
36310                                     cls : 'fa fa-chevron-right'
36311                                 }
36312                             ]
36313                         }
36314                     ]
36315                 }
36316             ]
36317         };
36318         
36319         return cfg;
36320     },
36321     
36322     initEvents : function()
36323     {
36324         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36325         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36326         
36327         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36328         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36329         
36330         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36331         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36332         
36333         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36334         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36335         
36336         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36337         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36338         
36339         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36340         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36341         
36342         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36343         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36344         
36345         this.thumbEl.on('click', this.onClick, this);
36346         
36347         this.prevIndicator.on('click', this.prev, this);
36348         
36349         this.nextIndicator.on('click', this.next, this);
36350         
36351     },
36352     
36353     initial : function()
36354     {
36355         if(this.files.length){
36356             this.indicator = 1;
36357             this.update()
36358         }
36359         
36360         this.fireEvent('initial', this);
36361     },
36362     
36363     update : function()
36364     {
36365         this.imageEl.attr('src', this.files[this.indicator - 1]);
36366         
36367         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36368         
36369         this.prevIndicator.show();
36370         
36371         if(this.indicator == 1){
36372             this.prevIndicator.hide();
36373         }
36374         
36375         this.nextIndicator.show();
36376         
36377         if(this.indicator == this.files.length){
36378             this.nextIndicator.hide();
36379         }
36380         
36381         this.thumbEl.scrollTo('top');
36382         
36383         this.fireEvent('update', this);
36384     },
36385     
36386     onClick : function(e)
36387     {
36388         e.preventDefault();
36389         
36390         this.fireEvent('click', this);
36391     },
36392     
36393     prev : function(e)
36394     {
36395         e.preventDefault();
36396         
36397         this.indicator = Math.max(1, this.indicator - 1);
36398         
36399         this.update();
36400     },
36401     
36402     next : function(e)
36403     {
36404         e.preventDefault();
36405         
36406         this.indicator = Math.min(this.files.length, this.indicator + 1);
36407         
36408         this.update();
36409     }
36410 });
36411 /*
36412  * - LGPL
36413  *
36414  * RadioSet
36415  *
36416  *
36417  */
36418
36419 /**
36420  * @class Roo.bootstrap.RadioSet
36421  * @extends Roo.bootstrap.Input
36422  * Bootstrap RadioSet class
36423  * @cfg {String} indicatorpos (left|right) default left
36424  * @cfg {Boolean} inline (true|false) inline the element (default true)
36425  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36426  * @constructor
36427  * Create a new RadioSet
36428  * @param {Object} config The config object
36429  */
36430
36431 Roo.bootstrap.RadioSet = function(config){
36432     
36433     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36434     
36435     this.radioes = [];
36436     
36437     Roo.bootstrap.RadioSet.register(this);
36438     
36439     this.addEvents({
36440         /**
36441         * @event check
36442         * Fires when the element is checked or unchecked.
36443         * @param {Roo.bootstrap.RadioSet} this This radio
36444         * @param {Roo.bootstrap.Radio} item The checked item
36445         */
36446        check : true,
36447        /**
36448         * @event click
36449         * Fires when the element is click.
36450         * @param {Roo.bootstrap.RadioSet} this This radio set
36451         * @param {Roo.bootstrap.Radio} item The checked item
36452         * @param {Roo.EventObject} e The event object
36453         */
36454        click : true
36455     });
36456     
36457 };
36458
36459 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36460
36461     radioes : false,
36462     
36463     inline : true,
36464     
36465     weight : '',
36466     
36467     indicatorpos : 'left',
36468     
36469     getAutoCreate : function()
36470     {
36471         var label = {
36472             tag : 'label',
36473             cls : 'roo-radio-set-label',
36474             cn : [
36475                 {
36476                     tag : 'span',
36477                     html : this.fieldLabel
36478                 }
36479             ]
36480         };
36481         if (Roo.bootstrap.version == 3) {
36482             
36483             
36484             if(this.indicatorpos == 'left'){
36485                 label.cn.unshift({
36486                     tag : 'i',
36487                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36488                     tooltip : 'This field is required'
36489                 });
36490             } else {
36491                 label.cn.push({
36492                     tag : 'i',
36493                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36494                     tooltip : 'This field is required'
36495                 });
36496             }
36497         }
36498         var items = {
36499             tag : 'div',
36500             cls : 'roo-radio-set-items'
36501         };
36502         
36503         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36504         
36505         if (align === 'left' && this.fieldLabel.length) {
36506             
36507             items = {
36508                 cls : "roo-radio-set-right", 
36509                 cn: [
36510                     items
36511                 ]
36512             };
36513             
36514             if(this.labelWidth > 12){
36515                 label.style = "width: " + this.labelWidth + 'px';
36516             }
36517             
36518             if(this.labelWidth < 13 && this.labelmd == 0){
36519                 this.labelmd = this.labelWidth;
36520             }
36521             
36522             if(this.labellg > 0){
36523                 label.cls += ' col-lg-' + this.labellg;
36524                 items.cls += ' col-lg-' + (12 - this.labellg);
36525             }
36526             
36527             if(this.labelmd > 0){
36528                 label.cls += ' col-md-' + this.labelmd;
36529                 items.cls += ' col-md-' + (12 - this.labelmd);
36530             }
36531             
36532             if(this.labelsm > 0){
36533                 label.cls += ' col-sm-' + this.labelsm;
36534                 items.cls += ' col-sm-' + (12 - this.labelsm);
36535             }
36536             
36537             if(this.labelxs > 0){
36538                 label.cls += ' col-xs-' + this.labelxs;
36539                 items.cls += ' col-xs-' + (12 - this.labelxs);
36540             }
36541         }
36542         
36543         var cfg = {
36544             tag : 'div',
36545             cls : 'roo-radio-set',
36546             cn : [
36547                 {
36548                     tag : 'input',
36549                     cls : 'roo-radio-set-input',
36550                     type : 'hidden',
36551                     name : this.name,
36552                     value : this.value ? this.value :  ''
36553                 },
36554                 label,
36555                 items
36556             ]
36557         };
36558         
36559         if(this.weight.length){
36560             cfg.cls += ' roo-radio-' + this.weight;
36561         }
36562         
36563         if(this.inline) {
36564             cfg.cls += ' roo-radio-set-inline';
36565         }
36566         
36567         var settings=this;
36568         ['xs','sm','md','lg'].map(function(size){
36569             if (settings[size]) {
36570                 cfg.cls += ' col-' + size + '-' + settings[size];
36571             }
36572         });
36573         
36574         return cfg;
36575         
36576     },
36577
36578     initEvents : function()
36579     {
36580         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36581         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36582         
36583         if(!this.fieldLabel.length){
36584             this.labelEl.hide();
36585         }
36586         
36587         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36588         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36589         
36590         this.indicator = this.indicatorEl();
36591         
36592         if(this.indicator){
36593             this.indicator.addClass('invisible');
36594         }
36595         
36596         this.originalValue = this.getValue();
36597         
36598     },
36599     
36600     inputEl: function ()
36601     {
36602         return this.el.select('.roo-radio-set-input', true).first();
36603     },
36604     
36605     getChildContainer : function()
36606     {
36607         return this.itemsEl;
36608     },
36609     
36610     register : function(item)
36611     {
36612         this.radioes.push(item);
36613         
36614     },
36615     
36616     validate : function()
36617     {   
36618         if(this.getVisibilityEl().hasClass('hidden')){
36619             return true;
36620         }
36621         
36622         var valid = false;
36623         
36624         Roo.each(this.radioes, function(i){
36625             if(!i.checked){
36626                 return;
36627             }
36628             
36629             valid = true;
36630             return false;
36631         });
36632         
36633         if(this.allowBlank) {
36634             return true;
36635         }
36636         
36637         if(this.disabled || valid){
36638             this.markValid();
36639             return true;
36640         }
36641         
36642         this.markInvalid();
36643         return false;
36644         
36645     },
36646     
36647     markValid : function()
36648     {
36649         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36650             this.indicatorEl().removeClass('visible');
36651             this.indicatorEl().addClass('invisible');
36652         }
36653         
36654         
36655         if (Roo.bootstrap.version == 3) {
36656             this.el.removeClass([this.invalidClass, this.validClass]);
36657             this.el.addClass(this.validClass);
36658         } else {
36659             this.el.removeClass(['is-invalid','is-valid']);
36660             this.el.addClass(['is-valid']);
36661         }
36662         this.fireEvent('valid', this);
36663     },
36664     
36665     markInvalid : function(msg)
36666     {
36667         if(this.allowBlank || this.disabled){
36668             return;
36669         }
36670         
36671         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36672             this.indicatorEl().removeClass('invisible');
36673             this.indicatorEl().addClass('visible');
36674         }
36675         if (Roo.bootstrap.version == 3) {
36676             this.el.removeClass([this.invalidClass, this.validClass]);
36677             this.el.addClass(this.invalidClass);
36678         } else {
36679             this.el.removeClass(['is-invalid','is-valid']);
36680             this.el.addClass(['is-invalid']);
36681         }
36682         
36683         this.fireEvent('invalid', this, msg);
36684         
36685     },
36686     
36687     setValue : function(v, suppressEvent)
36688     {   
36689         if(this.value === v){
36690             return;
36691         }
36692         
36693         this.value = v;
36694         
36695         if(this.rendered){
36696             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36697         }
36698         
36699         Roo.each(this.radioes, function(i){
36700             i.checked = false;
36701             i.el.removeClass('checked');
36702         });
36703         
36704         Roo.each(this.radioes, function(i){
36705             
36706             if(i.value === v || i.value.toString() === v.toString()){
36707                 i.checked = true;
36708                 i.el.addClass('checked');
36709                 
36710                 if(suppressEvent !== true){
36711                     this.fireEvent('check', this, i);
36712                 }
36713                 
36714                 return false;
36715             }
36716             
36717         }, this);
36718         
36719         this.validate();
36720     },
36721     
36722     clearInvalid : function(){
36723         
36724         if(!this.el || this.preventMark){
36725             return;
36726         }
36727         
36728         this.el.removeClass([this.invalidClass]);
36729         
36730         this.fireEvent('valid', this);
36731     }
36732     
36733 });
36734
36735 Roo.apply(Roo.bootstrap.RadioSet, {
36736     
36737     groups: {},
36738     
36739     register : function(set)
36740     {
36741         this.groups[set.name] = set;
36742     },
36743     
36744     get: function(name) 
36745     {
36746         if (typeof(this.groups[name]) == 'undefined') {
36747             return false;
36748         }
36749         
36750         return this.groups[name] ;
36751     }
36752     
36753 });
36754 /*
36755  * Based on:
36756  * Ext JS Library 1.1.1
36757  * Copyright(c) 2006-2007, Ext JS, LLC.
36758  *
36759  * Originally Released Under LGPL - original licence link has changed is not relivant.
36760  *
36761  * Fork - LGPL
36762  * <script type="text/javascript">
36763  */
36764
36765
36766 /**
36767  * @class Roo.bootstrap.SplitBar
36768  * @extends Roo.util.Observable
36769  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36770  * <br><br>
36771  * Usage:
36772  * <pre><code>
36773 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36774                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36775 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36776 split.minSize = 100;
36777 split.maxSize = 600;
36778 split.animate = true;
36779 split.on('moved', splitterMoved);
36780 </code></pre>
36781  * @constructor
36782  * Create a new SplitBar
36783  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36784  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36785  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36786  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36787                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36788                         position of the SplitBar).
36789  */
36790 Roo.bootstrap.SplitBar = function(cfg){
36791     
36792     /** @private */
36793     
36794     //{
36795     //  dragElement : elm
36796     //  resizingElement: el,
36797         // optional..
36798     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36799     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36800         // existingProxy ???
36801     //}
36802     
36803     this.el = Roo.get(cfg.dragElement, true);
36804     this.el.dom.unselectable = "on";
36805     /** @private */
36806     this.resizingEl = Roo.get(cfg.resizingElement, true);
36807
36808     /**
36809      * @private
36810      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36811      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36812      * @type Number
36813      */
36814     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36815     
36816     /**
36817      * The minimum size of the resizing element. (Defaults to 0)
36818      * @type Number
36819      */
36820     this.minSize = 0;
36821     
36822     /**
36823      * The maximum size of the resizing element. (Defaults to 2000)
36824      * @type Number
36825      */
36826     this.maxSize = 2000;
36827     
36828     /**
36829      * Whether to animate the transition to the new size
36830      * @type Boolean
36831      */
36832     this.animate = false;
36833     
36834     /**
36835      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36836      * @type Boolean
36837      */
36838     this.useShim = false;
36839     
36840     /** @private */
36841     this.shim = null;
36842     
36843     if(!cfg.existingProxy){
36844         /** @private */
36845         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36846     }else{
36847         this.proxy = Roo.get(cfg.existingProxy).dom;
36848     }
36849     /** @private */
36850     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36851     
36852     /** @private */
36853     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36854     
36855     /** @private */
36856     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36857     
36858     /** @private */
36859     this.dragSpecs = {};
36860     
36861     /**
36862      * @private The adapter to use to positon and resize elements
36863      */
36864     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36865     this.adapter.init(this);
36866     
36867     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36868         /** @private */
36869         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36870         this.el.addClass("roo-splitbar-h");
36871     }else{
36872         /** @private */
36873         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36874         this.el.addClass("roo-splitbar-v");
36875     }
36876     
36877     this.addEvents({
36878         /**
36879          * @event resize
36880          * Fires when the splitter is moved (alias for {@link #event-moved})
36881          * @param {Roo.bootstrap.SplitBar} this
36882          * @param {Number} newSize the new width or height
36883          */
36884         "resize" : true,
36885         /**
36886          * @event moved
36887          * Fires when the splitter is moved
36888          * @param {Roo.bootstrap.SplitBar} this
36889          * @param {Number} newSize the new width or height
36890          */
36891         "moved" : true,
36892         /**
36893          * @event beforeresize
36894          * Fires before the splitter is dragged
36895          * @param {Roo.bootstrap.SplitBar} this
36896          */
36897         "beforeresize" : true,
36898
36899         "beforeapply" : true
36900     });
36901
36902     Roo.util.Observable.call(this);
36903 };
36904
36905 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36906     onStartProxyDrag : function(x, y){
36907         this.fireEvent("beforeresize", this);
36908         if(!this.overlay){
36909             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36910             o.unselectable();
36911             o.enableDisplayMode("block");
36912             // all splitbars share the same overlay
36913             Roo.bootstrap.SplitBar.prototype.overlay = o;
36914         }
36915         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36916         this.overlay.show();
36917         Roo.get(this.proxy).setDisplayed("block");
36918         var size = this.adapter.getElementSize(this);
36919         this.activeMinSize = this.getMinimumSize();;
36920         this.activeMaxSize = this.getMaximumSize();;
36921         var c1 = size - this.activeMinSize;
36922         var c2 = Math.max(this.activeMaxSize - size, 0);
36923         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36924             this.dd.resetConstraints();
36925             this.dd.setXConstraint(
36926                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36927                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36928             );
36929             this.dd.setYConstraint(0, 0);
36930         }else{
36931             this.dd.resetConstraints();
36932             this.dd.setXConstraint(0, 0);
36933             this.dd.setYConstraint(
36934                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36935                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36936             );
36937          }
36938         this.dragSpecs.startSize = size;
36939         this.dragSpecs.startPoint = [x, y];
36940         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36941     },
36942     
36943     /** 
36944      * @private Called after the drag operation by the DDProxy
36945      */
36946     onEndProxyDrag : function(e){
36947         Roo.get(this.proxy).setDisplayed(false);
36948         var endPoint = Roo.lib.Event.getXY(e);
36949         if(this.overlay){
36950             this.overlay.hide();
36951         }
36952         var newSize;
36953         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36954             newSize = this.dragSpecs.startSize + 
36955                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36956                     endPoint[0] - this.dragSpecs.startPoint[0] :
36957                     this.dragSpecs.startPoint[0] - endPoint[0]
36958                 );
36959         }else{
36960             newSize = this.dragSpecs.startSize + 
36961                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36962                     endPoint[1] - this.dragSpecs.startPoint[1] :
36963                     this.dragSpecs.startPoint[1] - endPoint[1]
36964                 );
36965         }
36966         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36967         if(newSize != this.dragSpecs.startSize){
36968             if(this.fireEvent('beforeapply', this, newSize) !== false){
36969                 this.adapter.setElementSize(this, newSize);
36970                 this.fireEvent("moved", this, newSize);
36971                 this.fireEvent("resize", this, newSize);
36972             }
36973         }
36974     },
36975     
36976     /**
36977      * Get the adapter this SplitBar uses
36978      * @return The adapter object
36979      */
36980     getAdapter : function(){
36981         return this.adapter;
36982     },
36983     
36984     /**
36985      * Set the adapter this SplitBar uses
36986      * @param {Object} adapter A SplitBar adapter object
36987      */
36988     setAdapter : function(adapter){
36989         this.adapter = adapter;
36990         this.adapter.init(this);
36991     },
36992     
36993     /**
36994      * Gets the minimum size for the resizing element
36995      * @return {Number} The minimum size
36996      */
36997     getMinimumSize : function(){
36998         return this.minSize;
36999     },
37000     
37001     /**
37002      * Sets the minimum size for the resizing element
37003      * @param {Number} minSize The minimum size
37004      */
37005     setMinimumSize : function(minSize){
37006         this.minSize = minSize;
37007     },
37008     
37009     /**
37010      * Gets the maximum size for the resizing element
37011      * @return {Number} The maximum size
37012      */
37013     getMaximumSize : function(){
37014         return this.maxSize;
37015     },
37016     
37017     /**
37018      * Sets the maximum size for the resizing element
37019      * @param {Number} maxSize The maximum size
37020      */
37021     setMaximumSize : function(maxSize){
37022         this.maxSize = maxSize;
37023     },
37024     
37025     /**
37026      * Sets the initialize size for the resizing element
37027      * @param {Number} size The initial size
37028      */
37029     setCurrentSize : function(size){
37030         var oldAnimate = this.animate;
37031         this.animate = false;
37032         this.adapter.setElementSize(this, size);
37033         this.animate = oldAnimate;
37034     },
37035     
37036     /**
37037      * Destroy this splitbar. 
37038      * @param {Boolean} removeEl True to remove the element
37039      */
37040     destroy : function(removeEl){
37041         if(this.shim){
37042             this.shim.remove();
37043         }
37044         this.dd.unreg();
37045         this.proxy.parentNode.removeChild(this.proxy);
37046         if(removeEl){
37047             this.el.remove();
37048         }
37049     }
37050 });
37051
37052 /**
37053  * @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.
37054  */
37055 Roo.bootstrap.SplitBar.createProxy = function(dir){
37056     var proxy = new Roo.Element(document.createElement("div"));
37057     proxy.unselectable();
37058     var cls = 'roo-splitbar-proxy';
37059     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
37060     document.body.appendChild(proxy.dom);
37061     return proxy.dom;
37062 };
37063
37064 /** 
37065  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
37066  * Default Adapter. It assumes the splitter and resizing element are not positioned
37067  * elements and only gets/sets the width of the element. Generally used for table based layouts.
37068  */
37069 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
37070 };
37071
37072 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
37073     // do nothing for now
37074     init : function(s){
37075     
37076     },
37077     /**
37078      * Called before drag operations to get the current size of the resizing element. 
37079      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37080      */
37081      getElementSize : function(s){
37082         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37083             return s.resizingEl.getWidth();
37084         }else{
37085             return s.resizingEl.getHeight();
37086         }
37087     },
37088     
37089     /**
37090      * Called after drag operations to set the size of the resizing element.
37091      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
37092      * @param {Number} newSize The new size to set
37093      * @param {Function} onComplete A function to be invoked when resizing is complete
37094      */
37095     setElementSize : function(s, newSize, onComplete){
37096         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
37097             if(!s.animate){
37098                 s.resizingEl.setWidth(newSize);
37099                 if(onComplete){
37100                     onComplete(s, newSize);
37101                 }
37102             }else{
37103                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
37104             }
37105         }else{
37106             
37107             if(!s.animate){
37108                 s.resizingEl.setHeight(newSize);
37109                 if(onComplete){
37110                     onComplete(s, newSize);
37111                 }
37112             }else{
37113                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
37114             }
37115         }
37116     }
37117 };
37118
37119 /** 
37120  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
37121  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
37122  * Adapter that  moves the splitter element to align with the resized sizing element. 
37123  * Used with an absolute positioned SplitBar.
37124  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
37125  * document.body, make sure you assign an id to the body element.
37126  */
37127 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
37128     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
37129     this.container = Roo.get(container);
37130 };
37131
37132 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
37133     init : function(s){
37134         this.basic.init(s);
37135     },
37136     
37137     getElementSize : function(s){
37138         return this.basic.getElementSize(s);
37139     },
37140     
37141     setElementSize : function(s, newSize, onComplete){
37142         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37143     },
37144     
37145     moveSplitter : function(s){
37146         var yes = Roo.bootstrap.SplitBar;
37147         switch(s.placement){
37148             case yes.LEFT:
37149                 s.el.setX(s.resizingEl.getRight());
37150                 break;
37151             case yes.RIGHT:
37152                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37153                 break;
37154             case yes.TOP:
37155                 s.el.setY(s.resizingEl.getBottom());
37156                 break;
37157             case yes.BOTTOM:
37158                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37159                 break;
37160         }
37161     }
37162 };
37163
37164 /**
37165  * Orientation constant - Create a vertical SplitBar
37166  * @static
37167  * @type Number
37168  */
37169 Roo.bootstrap.SplitBar.VERTICAL = 1;
37170
37171 /**
37172  * Orientation constant - Create a horizontal SplitBar
37173  * @static
37174  * @type Number
37175  */
37176 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37177
37178 /**
37179  * Placement constant - The resizing element is to the left of the splitter element
37180  * @static
37181  * @type Number
37182  */
37183 Roo.bootstrap.SplitBar.LEFT = 1;
37184
37185 /**
37186  * Placement constant - The resizing element is to the right of the splitter element
37187  * @static
37188  * @type Number
37189  */
37190 Roo.bootstrap.SplitBar.RIGHT = 2;
37191
37192 /**
37193  * Placement constant - The resizing element is positioned above the splitter element
37194  * @static
37195  * @type Number
37196  */
37197 Roo.bootstrap.SplitBar.TOP = 3;
37198
37199 /**
37200  * Placement constant - The resizing element is positioned under splitter element
37201  * @static
37202  * @type Number
37203  */
37204 Roo.bootstrap.SplitBar.BOTTOM = 4;
37205 Roo.namespace("Roo.bootstrap.layout");/*
37206  * Based on:
37207  * Ext JS Library 1.1.1
37208  * Copyright(c) 2006-2007, Ext JS, LLC.
37209  *
37210  * Originally Released Under LGPL - original licence link has changed is not relivant.
37211  *
37212  * Fork - LGPL
37213  * <script type="text/javascript">
37214  */
37215
37216 /**
37217  * @class Roo.bootstrap.layout.Manager
37218  * @extends Roo.bootstrap.Component
37219  * Base class for layout managers.
37220  */
37221 Roo.bootstrap.layout.Manager = function(config)
37222 {
37223     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37224
37225
37226
37227
37228
37229     /** false to disable window resize monitoring @type Boolean */
37230     this.monitorWindowResize = true;
37231     this.regions = {};
37232     this.addEvents({
37233         /**
37234          * @event layout
37235          * Fires when a layout is performed.
37236          * @param {Roo.LayoutManager} this
37237          */
37238         "layout" : true,
37239         /**
37240          * @event regionresized
37241          * Fires when the user resizes a region.
37242          * @param {Roo.LayoutRegion} region The resized region
37243          * @param {Number} newSize The new size (width for east/west, height for north/south)
37244          */
37245         "regionresized" : true,
37246         /**
37247          * @event regioncollapsed
37248          * Fires when a region is collapsed.
37249          * @param {Roo.LayoutRegion} region The collapsed region
37250          */
37251         "regioncollapsed" : true,
37252         /**
37253          * @event regionexpanded
37254          * Fires when a region is expanded.
37255          * @param {Roo.LayoutRegion} region The expanded region
37256          */
37257         "regionexpanded" : true
37258     });
37259     this.updating = false;
37260
37261     if (config.el) {
37262         this.el = Roo.get(config.el);
37263         this.initEvents();
37264     }
37265
37266 };
37267
37268 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37269
37270
37271     regions : null,
37272
37273     monitorWindowResize : true,
37274
37275
37276     updating : false,
37277
37278
37279     onRender : function(ct, position)
37280     {
37281         if(!this.el){
37282             this.el = Roo.get(ct);
37283             this.initEvents();
37284         }
37285         //this.fireEvent('render',this);
37286     },
37287
37288
37289     initEvents: function()
37290     {
37291
37292
37293         // ie scrollbar fix
37294         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37295             document.body.scroll = "no";
37296         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37297             this.el.position('relative');
37298         }
37299         this.id = this.el.id;
37300         this.el.addClass("roo-layout-container");
37301         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37302         if(this.el.dom != document.body ) {
37303             this.el.on('resize', this.layout,this);
37304             this.el.on('show', this.layout,this);
37305         }
37306
37307     },
37308
37309     /**
37310      * Returns true if this layout is currently being updated
37311      * @return {Boolean}
37312      */
37313     isUpdating : function(){
37314         return this.updating;
37315     },
37316
37317     /**
37318      * Suspend the LayoutManager from doing auto-layouts while
37319      * making multiple add or remove calls
37320      */
37321     beginUpdate : function(){
37322         this.updating = true;
37323     },
37324
37325     /**
37326      * Restore auto-layouts and optionally disable the manager from performing a layout
37327      * @param {Boolean} noLayout true to disable a layout update
37328      */
37329     endUpdate : function(noLayout){
37330         this.updating = false;
37331         if(!noLayout){
37332             this.layout();
37333         }
37334     },
37335
37336     layout: function(){
37337         // abstract...
37338     },
37339
37340     onRegionResized : function(region, newSize){
37341         this.fireEvent("regionresized", region, newSize);
37342         this.layout();
37343     },
37344
37345     onRegionCollapsed : function(region){
37346         this.fireEvent("regioncollapsed", region);
37347     },
37348
37349     onRegionExpanded : function(region){
37350         this.fireEvent("regionexpanded", region);
37351     },
37352
37353     /**
37354      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37355      * performs box-model adjustments.
37356      * @return {Object} The size as an object {width: (the width), height: (the height)}
37357      */
37358     getViewSize : function()
37359     {
37360         var size;
37361         if(this.el.dom != document.body){
37362             size = this.el.getSize();
37363         }else{
37364             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37365         }
37366         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37367         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37368         return size;
37369     },
37370
37371     /**
37372      * Returns the Element this layout is bound to.
37373      * @return {Roo.Element}
37374      */
37375     getEl : function(){
37376         return this.el;
37377     },
37378
37379     /**
37380      * Returns the specified region.
37381      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37382      * @return {Roo.LayoutRegion}
37383      */
37384     getRegion : function(target){
37385         return this.regions[target.toLowerCase()];
37386     },
37387
37388     onWindowResize : function(){
37389         if(this.monitorWindowResize){
37390             this.layout();
37391         }
37392     }
37393 });
37394 /*
37395  * Based on:
37396  * Ext JS Library 1.1.1
37397  * Copyright(c) 2006-2007, Ext JS, LLC.
37398  *
37399  * Originally Released Under LGPL - original licence link has changed is not relivant.
37400  *
37401  * Fork - LGPL
37402  * <script type="text/javascript">
37403  */
37404 /**
37405  * @class Roo.bootstrap.layout.Border
37406  * @extends Roo.bootstrap.layout.Manager
37407  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37408  * please see: examples/bootstrap/nested.html<br><br>
37409  
37410 <b>The container the layout is rendered into can be either the body element or any other element.
37411 If it is not the body element, the container needs to either be an absolute positioned element,
37412 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37413 the container size if it is not the body element.</b>
37414
37415 * @constructor
37416 * Create a new Border
37417 * @param {Object} config Configuration options
37418  */
37419 Roo.bootstrap.layout.Border = function(config){
37420     config = config || {};
37421     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37422     
37423     
37424     
37425     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37426         if(config[region]){
37427             config[region].region = region;
37428             this.addRegion(config[region]);
37429         }
37430     },this);
37431     
37432 };
37433
37434 Roo.bootstrap.layout.Border.regions =  ["center", "north","south","east","west"];
37435
37436 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37437     
37438     parent : false, // this might point to a 'nest' or a ???
37439     
37440     /**
37441      * Creates and adds a new region if it doesn't already exist.
37442      * @param {String} target The target region key (north, south, east, west or center).
37443      * @param {Object} config The regions config object
37444      * @return {BorderLayoutRegion} The new region
37445      */
37446     addRegion : function(config)
37447     {
37448         if(!this.regions[config.region]){
37449             var r = this.factory(config);
37450             this.bindRegion(r);
37451         }
37452         return this.regions[config.region];
37453     },
37454
37455     // private (kinda)
37456     bindRegion : function(r){
37457         this.regions[r.config.region] = r;
37458         
37459         r.on("visibilitychange",    this.layout, this);
37460         r.on("paneladded",          this.layout, this);
37461         r.on("panelremoved",        this.layout, this);
37462         r.on("invalidated",         this.layout, this);
37463         r.on("resized",             this.onRegionResized, this);
37464         r.on("collapsed",           this.onRegionCollapsed, this);
37465         r.on("expanded",            this.onRegionExpanded, this);
37466     },
37467
37468     /**
37469      * Performs a layout update.
37470      */
37471     layout : function()
37472     {
37473         if(this.updating) {
37474             return;
37475         }
37476         
37477         // render all the rebions if they have not been done alreayd?
37478         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37479             if(this.regions[region] && !this.regions[region].bodyEl){
37480                 this.regions[region].onRender(this.el)
37481             }
37482         },this);
37483         
37484         var size = this.getViewSize();
37485         var w = size.width;
37486         var h = size.height;
37487         var centerW = w;
37488         var centerH = h;
37489         var centerY = 0;
37490         var centerX = 0;
37491         //var x = 0, y = 0;
37492
37493         var rs = this.regions;
37494         var north = rs["north"];
37495         var south = rs["south"]; 
37496         var west = rs["west"];
37497         var east = rs["east"];
37498         var center = rs["center"];
37499         //if(this.hideOnLayout){ // not supported anymore
37500             //c.el.setStyle("display", "none");
37501         //}
37502         if(north && north.isVisible()){
37503             var b = north.getBox();
37504             var m = north.getMargins();
37505             b.width = w - (m.left+m.right);
37506             b.x = m.left;
37507             b.y = m.top;
37508             centerY = b.height + b.y + m.bottom;
37509             centerH -= centerY;
37510             north.updateBox(this.safeBox(b));
37511         }
37512         if(south && south.isVisible()){
37513             var b = south.getBox();
37514             var m = south.getMargins();
37515             b.width = w - (m.left+m.right);
37516             b.x = m.left;
37517             var totalHeight = (b.height + m.top + m.bottom);
37518             b.y = h - totalHeight + m.top;
37519             centerH -= totalHeight;
37520             south.updateBox(this.safeBox(b));
37521         }
37522         if(west && west.isVisible()){
37523             var b = west.getBox();
37524             var m = west.getMargins();
37525             b.height = centerH - (m.top+m.bottom);
37526             b.x = m.left;
37527             b.y = centerY + m.top;
37528             var totalWidth = (b.width + m.left + m.right);
37529             centerX += totalWidth;
37530             centerW -= totalWidth;
37531             west.updateBox(this.safeBox(b));
37532         }
37533         if(east && east.isVisible()){
37534             var b = east.getBox();
37535             var m = east.getMargins();
37536             b.height = centerH - (m.top+m.bottom);
37537             var totalWidth = (b.width + m.left + m.right);
37538             b.x = w - totalWidth + m.left;
37539             b.y = centerY + m.top;
37540             centerW -= totalWidth;
37541             east.updateBox(this.safeBox(b));
37542         }
37543         if(center){
37544             var m = center.getMargins();
37545             var centerBox = {
37546                 x: centerX + m.left,
37547                 y: centerY + m.top,
37548                 width: centerW - (m.left+m.right),
37549                 height: centerH - (m.top+m.bottom)
37550             };
37551             //if(this.hideOnLayout){
37552                 //center.el.setStyle("display", "block");
37553             //}
37554             center.updateBox(this.safeBox(centerBox));
37555         }
37556         this.el.repaint();
37557         this.fireEvent("layout", this);
37558     },
37559
37560     // private
37561     safeBox : function(box){
37562         box.width = Math.max(0, box.width);
37563         box.height = Math.max(0, box.height);
37564         return box;
37565     },
37566
37567     /**
37568      * Adds a ContentPanel (or subclass) to this layout.
37569      * @param {String} target The target region key (north, south, east, west or center).
37570      * @param {Roo.ContentPanel} panel The panel to add
37571      * @return {Roo.ContentPanel} The added panel
37572      */
37573     add : function(target, panel){
37574          
37575         target = target.toLowerCase();
37576         return this.regions[target].add(panel);
37577     },
37578
37579     /**
37580      * Remove a ContentPanel (or subclass) to this layout.
37581      * @param {String} target The target region key (north, south, east, west or center).
37582      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37583      * @return {Roo.ContentPanel} The removed panel
37584      */
37585     remove : function(target, panel){
37586         target = target.toLowerCase();
37587         return this.regions[target].remove(panel);
37588     },
37589
37590     /**
37591      * Searches all regions for a panel with the specified id
37592      * @param {String} panelId
37593      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37594      */
37595     findPanel : function(panelId){
37596         var rs = this.regions;
37597         for(var target in rs){
37598             if(typeof rs[target] != "function"){
37599                 var p = rs[target].getPanel(panelId);
37600                 if(p){
37601                     return p;
37602                 }
37603             }
37604         }
37605         return null;
37606     },
37607
37608     /**
37609      * Searches all regions for a panel with the specified id and activates (shows) it.
37610      * @param {String/ContentPanel} panelId The panels id or the panel itself
37611      * @return {Roo.ContentPanel} The shown panel or null
37612      */
37613     showPanel : function(panelId) {
37614       var rs = this.regions;
37615       for(var target in rs){
37616          var r = rs[target];
37617          if(typeof r != "function"){
37618             if(r.hasPanel(panelId)){
37619                return r.showPanel(panelId);
37620             }
37621          }
37622       }
37623       return null;
37624    },
37625
37626    /**
37627      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37628      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37629      */
37630    /*
37631     restoreState : function(provider){
37632         if(!provider){
37633             provider = Roo.state.Manager;
37634         }
37635         var sm = new Roo.LayoutStateManager();
37636         sm.init(this, provider);
37637     },
37638 */
37639  
37640  
37641     /**
37642      * Adds a xtype elements to the layout.
37643      * <pre><code>
37644
37645 layout.addxtype({
37646        xtype : 'ContentPanel',
37647        region: 'west',
37648        items: [ .... ]
37649    }
37650 );
37651
37652 layout.addxtype({
37653         xtype : 'NestedLayoutPanel',
37654         region: 'west',
37655         layout: {
37656            center: { },
37657            west: { }   
37658         },
37659         items : [ ... list of content panels or nested layout panels.. ]
37660    }
37661 );
37662 </code></pre>
37663      * @param {Object} cfg Xtype definition of item to add.
37664      */
37665     addxtype : function(cfg)
37666     {
37667         // basically accepts a pannel...
37668         // can accept a layout region..!?!?
37669         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37670         
37671         
37672         // theory?  children can only be panels??
37673         
37674         //if (!cfg.xtype.match(/Panel$/)) {
37675         //    return false;
37676         //}
37677         var ret = false;
37678         
37679         if (typeof(cfg.region) == 'undefined') {
37680             Roo.log("Failed to add Panel, region was not set");
37681             Roo.log(cfg);
37682             return false;
37683         }
37684         var region = cfg.region;
37685         delete cfg.region;
37686         
37687           
37688         var xitems = [];
37689         if (cfg.items) {
37690             xitems = cfg.items;
37691             delete cfg.items;
37692         }
37693         var nb = false;
37694         
37695         if ( region == 'center') {
37696             Roo.log("Center: " + cfg.title);
37697         }
37698         
37699         
37700         switch(cfg.xtype) 
37701         {
37702             case 'Content':  // ContentPanel (el, cfg)
37703             case 'Scroll':  // ContentPanel (el, cfg)
37704             case 'View': 
37705                 cfg.autoCreate = cfg.autoCreate || true;
37706                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37707                 //} else {
37708                 //    var el = this.el.createChild();
37709                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37710                 //}
37711                 
37712                 this.add(region, ret);
37713                 break;
37714             
37715             /*
37716             case 'TreePanel': // our new panel!
37717                 cfg.el = this.el.createChild();
37718                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37719                 this.add(region, ret);
37720                 break;
37721             */
37722             
37723             case 'Nest': 
37724                 // create a new Layout (which is  a Border Layout...
37725                 
37726                 var clayout = cfg.layout;
37727                 clayout.el  = this.el.createChild();
37728                 clayout.items   = clayout.items  || [];
37729                 
37730                 delete cfg.layout;
37731                 
37732                 // replace this exitems with the clayout ones..
37733                 xitems = clayout.items;
37734                  
37735                 // force background off if it's in center...
37736                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37737                     cfg.background = false;
37738                 }
37739                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37740                 
37741                 
37742                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37743                 //console.log('adding nested layout panel '  + cfg.toSource());
37744                 this.add(region, ret);
37745                 nb = {}; /// find first...
37746                 break;
37747             
37748             case 'Grid':
37749                 
37750                 // needs grid and region
37751                 
37752                 //var el = this.getRegion(region).el.createChild();
37753                 /*
37754                  *var el = this.el.createChild();
37755                 // create the grid first...
37756                 cfg.grid.container = el;
37757                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37758                 */
37759                 
37760                 if (region == 'center' && this.active ) {
37761                     cfg.background = false;
37762                 }
37763                 
37764                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37765                 
37766                 this.add(region, ret);
37767                 /*
37768                 if (cfg.background) {
37769                     // render grid on panel activation (if panel background)
37770                     ret.on('activate', function(gp) {
37771                         if (!gp.grid.rendered) {
37772                     //        gp.grid.render(el);
37773                         }
37774                     });
37775                 } else {
37776                   //  cfg.grid.render(el);
37777                 }
37778                 */
37779                 break;
37780            
37781            
37782             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37783                 // it was the old xcomponent building that caused this before.
37784                 // espeically if border is the top element in the tree.
37785                 ret = this;
37786                 break; 
37787                 
37788                     
37789                 
37790                 
37791                 
37792             default:
37793                 /*
37794                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37795                     
37796                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37797                     this.add(region, ret);
37798                 } else {
37799                 */
37800                     Roo.log(cfg);
37801                     throw "Can not add '" + cfg.xtype + "' to Border";
37802                     return null;
37803              
37804                                 
37805              
37806         }
37807         this.beginUpdate();
37808         // add children..
37809         var region = '';
37810         var abn = {};
37811         Roo.each(xitems, function(i)  {
37812             region = nb && i.region ? i.region : false;
37813             
37814             var add = ret.addxtype(i);
37815            
37816             if (region) {
37817                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37818                 if (!i.background) {
37819                     abn[region] = nb[region] ;
37820                 }
37821             }
37822             
37823         });
37824         this.endUpdate();
37825
37826         // make the last non-background panel active..
37827         //if (nb) { Roo.log(abn); }
37828         if (nb) {
37829             
37830             for(var r in abn) {
37831                 region = this.getRegion(r);
37832                 if (region) {
37833                     // tried using nb[r], but it does not work..
37834                      
37835                     region.showPanel(abn[r]);
37836                    
37837                 }
37838             }
37839         }
37840         return ret;
37841         
37842     },
37843     
37844     
37845 // private
37846     factory : function(cfg)
37847     {
37848         
37849         var validRegions = Roo.bootstrap.layout.Border.regions;
37850
37851         var target = cfg.region;
37852         cfg.mgr = this;
37853         
37854         var r = Roo.bootstrap.layout;
37855         Roo.log(target);
37856         switch(target){
37857             case "north":
37858                 return new r.North(cfg);
37859             case "south":
37860                 return new r.South(cfg);
37861             case "east":
37862                 return new r.East(cfg);
37863             case "west":
37864                 return new r.West(cfg);
37865             case "center":
37866                 return new r.Center(cfg);
37867         }
37868         throw 'Layout region "'+target+'" not supported.';
37869     }
37870     
37871     
37872 });
37873  /*
37874  * Based on:
37875  * Ext JS Library 1.1.1
37876  * Copyright(c) 2006-2007, Ext JS, LLC.
37877  *
37878  * Originally Released Under LGPL - original licence link has changed is not relivant.
37879  *
37880  * Fork - LGPL
37881  * <script type="text/javascript">
37882  */
37883  
37884 /**
37885  * @class Roo.bootstrap.layout.Basic
37886  * @extends Roo.util.Observable
37887  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37888  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37889  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37890  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37891  * @cfg {string}   region  the region that it inhabits..
37892  * @cfg {bool}   skipConfig skip config?
37893  * 
37894
37895  */
37896 Roo.bootstrap.layout.Basic = function(config){
37897     
37898     this.mgr = config.mgr;
37899     
37900     this.position = config.region;
37901     
37902     var skipConfig = config.skipConfig;
37903     
37904     this.events = {
37905         /**
37906          * @scope Roo.BasicLayoutRegion
37907          */
37908         
37909         /**
37910          * @event beforeremove
37911          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37912          * @param {Roo.LayoutRegion} this
37913          * @param {Roo.ContentPanel} panel The panel
37914          * @param {Object} e The cancel event object
37915          */
37916         "beforeremove" : true,
37917         /**
37918          * @event invalidated
37919          * Fires when the layout for this region is changed.
37920          * @param {Roo.LayoutRegion} this
37921          */
37922         "invalidated" : true,
37923         /**
37924          * @event visibilitychange
37925          * Fires when this region is shown or hidden 
37926          * @param {Roo.LayoutRegion} this
37927          * @param {Boolean} visibility true or false
37928          */
37929         "visibilitychange" : true,
37930         /**
37931          * @event paneladded
37932          * Fires when a panel is added. 
37933          * @param {Roo.LayoutRegion} this
37934          * @param {Roo.ContentPanel} panel The panel
37935          */
37936         "paneladded" : true,
37937         /**
37938          * @event panelremoved
37939          * Fires when a panel is removed. 
37940          * @param {Roo.LayoutRegion} this
37941          * @param {Roo.ContentPanel} panel The panel
37942          */
37943         "panelremoved" : true,
37944         /**
37945          * @event beforecollapse
37946          * Fires when this region before collapse.
37947          * @param {Roo.LayoutRegion} this
37948          */
37949         "beforecollapse" : true,
37950         /**
37951          * @event collapsed
37952          * Fires when this region is collapsed.
37953          * @param {Roo.LayoutRegion} this
37954          */
37955         "collapsed" : true,
37956         /**
37957          * @event expanded
37958          * Fires when this region is expanded.
37959          * @param {Roo.LayoutRegion} this
37960          */
37961         "expanded" : true,
37962         /**
37963          * @event slideshow
37964          * Fires when this region is slid into view.
37965          * @param {Roo.LayoutRegion} this
37966          */
37967         "slideshow" : true,
37968         /**
37969          * @event slidehide
37970          * Fires when this region slides out of view. 
37971          * @param {Roo.LayoutRegion} this
37972          */
37973         "slidehide" : true,
37974         /**
37975          * @event panelactivated
37976          * Fires when a panel is activated. 
37977          * @param {Roo.LayoutRegion} this
37978          * @param {Roo.ContentPanel} panel The activated panel
37979          */
37980         "panelactivated" : true,
37981         /**
37982          * @event resized
37983          * Fires when the user resizes this region. 
37984          * @param {Roo.LayoutRegion} this
37985          * @param {Number} newSize The new size (width for east/west, height for north/south)
37986          */
37987         "resized" : true
37988     };
37989     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37990     this.panels = new Roo.util.MixedCollection();
37991     this.panels.getKey = this.getPanelId.createDelegate(this);
37992     this.box = null;
37993     this.activePanel = null;
37994     // ensure listeners are added...
37995     
37996     if (config.listeners || config.events) {
37997         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37998             listeners : config.listeners || {},
37999             events : config.events || {}
38000         });
38001     }
38002     
38003     if(skipConfig !== true){
38004         this.applyConfig(config);
38005     }
38006 };
38007
38008 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
38009 {
38010     getPanelId : function(p){
38011         return p.getId();
38012     },
38013     
38014     applyConfig : function(config){
38015         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38016         this.config = config;
38017         
38018     },
38019     
38020     /**
38021      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
38022      * the width, for horizontal (north, south) the height.
38023      * @param {Number} newSize The new width or height
38024      */
38025     resizeTo : function(newSize){
38026         var el = this.el ? this.el :
38027                  (this.activePanel ? this.activePanel.getEl() : null);
38028         if(el){
38029             switch(this.position){
38030                 case "east":
38031                 case "west":
38032                     el.setWidth(newSize);
38033                     this.fireEvent("resized", this, newSize);
38034                 break;
38035                 case "north":
38036                 case "south":
38037                     el.setHeight(newSize);
38038                     this.fireEvent("resized", this, newSize);
38039                 break;                
38040             }
38041         }
38042     },
38043     
38044     getBox : function(){
38045         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
38046     },
38047     
38048     getMargins : function(){
38049         return this.margins;
38050     },
38051     
38052     updateBox : function(box){
38053         this.box = box;
38054         var el = this.activePanel.getEl();
38055         el.dom.style.left = box.x + "px";
38056         el.dom.style.top = box.y + "px";
38057         this.activePanel.setSize(box.width, box.height);
38058     },
38059     
38060     /**
38061      * Returns the container element for this region.
38062      * @return {Roo.Element}
38063      */
38064     getEl : function(){
38065         return this.activePanel;
38066     },
38067     
38068     /**
38069      * Returns true if this region is currently visible.
38070      * @return {Boolean}
38071      */
38072     isVisible : function(){
38073         return this.activePanel ? true : false;
38074     },
38075     
38076     setActivePanel : function(panel){
38077         panel = this.getPanel(panel);
38078         if(this.activePanel && this.activePanel != panel){
38079             this.activePanel.setActiveState(false);
38080             this.activePanel.getEl().setLeftTop(-10000,-10000);
38081         }
38082         this.activePanel = panel;
38083         panel.setActiveState(true);
38084         if(this.box){
38085             panel.setSize(this.box.width, this.box.height);
38086         }
38087         this.fireEvent("panelactivated", this, panel);
38088         this.fireEvent("invalidated");
38089     },
38090     
38091     /**
38092      * Show the specified panel.
38093      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
38094      * @return {Roo.ContentPanel} The shown panel or null
38095      */
38096     showPanel : function(panel){
38097         panel = this.getPanel(panel);
38098         if(panel){
38099             this.setActivePanel(panel);
38100         }
38101         return panel;
38102     },
38103     
38104     /**
38105      * Get the active panel for this region.
38106      * @return {Roo.ContentPanel} The active panel or null
38107      */
38108     getActivePanel : function(){
38109         return this.activePanel;
38110     },
38111     
38112     /**
38113      * Add the passed ContentPanel(s)
38114      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38115      * @return {Roo.ContentPanel} The panel added (if only one was added)
38116      */
38117     add : function(panel){
38118         if(arguments.length > 1){
38119             for(var i = 0, len = arguments.length; i < len; i++) {
38120                 this.add(arguments[i]);
38121             }
38122             return null;
38123         }
38124         if(this.hasPanel(panel)){
38125             this.showPanel(panel);
38126             return panel;
38127         }
38128         var el = panel.getEl();
38129         if(el.dom.parentNode != this.mgr.el.dom){
38130             this.mgr.el.dom.appendChild(el.dom);
38131         }
38132         if(panel.setRegion){
38133             panel.setRegion(this);
38134         }
38135         this.panels.add(panel);
38136         el.setStyle("position", "absolute");
38137         if(!panel.background){
38138             this.setActivePanel(panel);
38139             if(this.config.initialSize && this.panels.getCount()==1){
38140                 this.resizeTo(this.config.initialSize);
38141             }
38142         }
38143         this.fireEvent("paneladded", this, panel);
38144         return panel;
38145     },
38146     
38147     /**
38148      * Returns true if the panel is in this region.
38149      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38150      * @return {Boolean}
38151      */
38152     hasPanel : function(panel){
38153         if(typeof panel == "object"){ // must be panel obj
38154             panel = panel.getId();
38155         }
38156         return this.getPanel(panel) ? true : false;
38157     },
38158     
38159     /**
38160      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38161      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38162      * @param {Boolean} preservePanel Overrides the config preservePanel option
38163      * @return {Roo.ContentPanel} The panel that was removed
38164      */
38165     remove : function(panel, preservePanel){
38166         panel = this.getPanel(panel);
38167         if(!panel){
38168             return null;
38169         }
38170         var e = {};
38171         this.fireEvent("beforeremove", this, panel, e);
38172         if(e.cancel === true){
38173             return null;
38174         }
38175         var panelId = panel.getId();
38176         this.panels.removeKey(panelId);
38177         return panel;
38178     },
38179     
38180     /**
38181      * Returns the panel specified or null if it's not in this region.
38182      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38183      * @return {Roo.ContentPanel}
38184      */
38185     getPanel : function(id){
38186         if(typeof id == "object"){ // must be panel obj
38187             return id;
38188         }
38189         return this.panels.get(id);
38190     },
38191     
38192     /**
38193      * Returns this regions position (north/south/east/west/center).
38194      * @return {String} 
38195      */
38196     getPosition: function(){
38197         return this.position;    
38198     }
38199 });/*
38200  * Based on:
38201  * Ext JS Library 1.1.1
38202  * Copyright(c) 2006-2007, Ext JS, LLC.
38203  *
38204  * Originally Released Under LGPL - original licence link has changed is not relivant.
38205  *
38206  * Fork - LGPL
38207  * <script type="text/javascript">
38208  */
38209  
38210 /**
38211  * @class Roo.bootstrap.layout.Region
38212  * @extends Roo.bootstrap.layout.Basic
38213  * This class represents a region in a layout manager.
38214  
38215  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38216  * @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})
38217  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38218  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38219  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38220  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38221  * @cfg {String}    title           The title for the region (overrides panel titles)
38222  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38223  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38224  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38225  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38226  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38227  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38228  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38229  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38230  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38231  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38232
38233  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38234  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38235  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38236  * @cfg {Number}    width           For East/West panels
38237  * @cfg {Number}    height          For North/South panels
38238  * @cfg {Boolean}   split           To show the splitter
38239  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38240  * 
38241  * @cfg {string}   cls             Extra CSS classes to add to region
38242  * 
38243  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38244  * @cfg {string}   region  the region that it inhabits..
38245  *
38246
38247  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38248  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38249
38250  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38251  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38252  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38253  */
38254 Roo.bootstrap.layout.Region = function(config)
38255 {
38256     this.applyConfig(config);
38257
38258     var mgr = config.mgr;
38259     var pos = config.region;
38260     config.skipConfig = true;
38261     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38262     
38263     if (mgr.el) {
38264         this.onRender(mgr.el);   
38265     }
38266      
38267     this.visible = true;
38268     this.collapsed = false;
38269     this.unrendered_panels = [];
38270 };
38271
38272 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38273
38274     position: '', // set by wrapper (eg. north/south etc..)
38275     unrendered_panels : null,  // unrendered panels.
38276     
38277     tabPosition : false,
38278     
38279     mgr: false, // points to 'Border'
38280     
38281     
38282     createBody : function(){
38283         /** This region's body element 
38284         * @type Roo.Element */
38285         this.bodyEl = this.el.createChild({
38286                 tag: "div",
38287                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38288         });
38289     },
38290
38291     onRender: function(ctr, pos)
38292     {
38293         var dh = Roo.DomHelper;
38294         /** This region's container element 
38295         * @type Roo.Element */
38296         this.el = dh.append(ctr.dom, {
38297                 tag: "div",
38298                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38299             }, true);
38300         /** This region's title element 
38301         * @type Roo.Element */
38302     
38303         this.titleEl = dh.append(this.el.dom,  {
38304                 tag: "div",
38305                 unselectable: "on",
38306                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38307                 children:[
38308                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38309                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38310                 ]
38311             }, true);
38312         
38313         this.titleEl.enableDisplayMode();
38314         /** This region's title text element 
38315         * @type HTMLElement */
38316         this.titleTextEl = this.titleEl.dom.firstChild;
38317         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38318         /*
38319         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38320         this.closeBtn.enableDisplayMode();
38321         this.closeBtn.on("click", this.closeClicked, this);
38322         this.closeBtn.hide();
38323     */
38324         this.createBody(this.config);
38325         if(this.config.hideWhenEmpty){
38326             this.hide();
38327             this.on("paneladded", this.validateVisibility, this);
38328             this.on("panelremoved", this.validateVisibility, this);
38329         }
38330         if(this.autoScroll){
38331             this.bodyEl.setStyle("overflow", "auto");
38332         }else{
38333             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38334         }
38335         //if(c.titlebar !== false){
38336             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38337                 this.titleEl.hide();
38338             }else{
38339                 this.titleEl.show();
38340                 if(this.config.title){
38341                     this.titleTextEl.innerHTML = this.config.title;
38342                 }
38343             }
38344         //}
38345         if(this.config.collapsed){
38346             this.collapse(true);
38347         }
38348         if(this.config.hidden){
38349             this.hide();
38350         }
38351         
38352         if (this.unrendered_panels && this.unrendered_panels.length) {
38353             for (var i =0;i< this.unrendered_panels.length; i++) {
38354                 this.add(this.unrendered_panels[i]);
38355             }
38356             this.unrendered_panels = null;
38357             
38358         }
38359         
38360     },
38361     
38362     applyConfig : function(c)
38363     {
38364         /*
38365          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38366             var dh = Roo.DomHelper;
38367             if(c.titlebar !== false){
38368                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38369                 this.collapseBtn.on("click", this.collapse, this);
38370                 this.collapseBtn.enableDisplayMode();
38371                 /*
38372                 if(c.showPin === true || this.showPin){
38373                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38374                     this.stickBtn.enableDisplayMode();
38375                     this.stickBtn.on("click", this.expand, this);
38376                     this.stickBtn.hide();
38377                 }
38378                 
38379             }
38380             */
38381             /** This region's collapsed element
38382             * @type Roo.Element */
38383             /*
38384              *
38385             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38386                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38387             ]}, true);
38388             
38389             if(c.floatable !== false){
38390                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38391                this.collapsedEl.on("click", this.collapseClick, this);
38392             }
38393
38394             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38395                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38396                    id: "message", unselectable: "on", style:{"float":"left"}});
38397                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38398              }
38399             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38400             this.expandBtn.on("click", this.expand, this);
38401             
38402         }
38403         
38404         if(this.collapseBtn){
38405             this.collapseBtn.setVisible(c.collapsible == true);
38406         }
38407         
38408         this.cmargins = c.cmargins || this.cmargins ||
38409                          (this.position == "west" || this.position == "east" ?
38410                              {top: 0, left: 2, right:2, bottom: 0} :
38411                              {top: 2, left: 0, right:0, bottom: 2});
38412         */
38413         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38414         
38415         
38416         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38417         
38418         this.autoScroll = c.autoScroll || false;
38419         
38420         
38421        
38422         
38423         this.duration = c.duration || .30;
38424         this.slideDuration = c.slideDuration || .45;
38425         this.config = c;
38426        
38427     },
38428     /**
38429      * Returns true if this region is currently visible.
38430      * @return {Boolean}
38431      */
38432     isVisible : function(){
38433         return this.visible;
38434     },
38435
38436     /**
38437      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38438      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38439      */
38440     //setCollapsedTitle : function(title){
38441     //    title = title || "&#160;";
38442      //   if(this.collapsedTitleTextEl){
38443       //      this.collapsedTitleTextEl.innerHTML = title;
38444        // }
38445     //},
38446
38447     getBox : function(){
38448         var b;
38449       //  if(!this.collapsed){
38450             b = this.el.getBox(false, true);
38451        // }else{
38452           //  b = this.collapsedEl.getBox(false, true);
38453         //}
38454         return b;
38455     },
38456
38457     getMargins : function(){
38458         return this.margins;
38459         //return this.collapsed ? this.cmargins : this.margins;
38460     },
38461 /*
38462     highlight : function(){
38463         this.el.addClass("x-layout-panel-dragover");
38464     },
38465
38466     unhighlight : function(){
38467         this.el.removeClass("x-layout-panel-dragover");
38468     },
38469 */
38470     updateBox : function(box)
38471     {
38472         if (!this.bodyEl) {
38473             return; // not rendered yet..
38474         }
38475         
38476         this.box = box;
38477         if(!this.collapsed){
38478             this.el.dom.style.left = box.x + "px";
38479             this.el.dom.style.top = box.y + "px";
38480             this.updateBody(box.width, box.height);
38481         }else{
38482             this.collapsedEl.dom.style.left = box.x + "px";
38483             this.collapsedEl.dom.style.top = box.y + "px";
38484             this.collapsedEl.setSize(box.width, box.height);
38485         }
38486         if(this.tabs){
38487             this.tabs.autoSizeTabs();
38488         }
38489     },
38490
38491     updateBody : function(w, h)
38492     {
38493         if(w !== null){
38494             this.el.setWidth(w);
38495             w -= this.el.getBorderWidth("rl");
38496             if(this.config.adjustments){
38497                 w += this.config.adjustments[0];
38498             }
38499         }
38500         if(h !== null && h > 0){
38501             this.el.setHeight(h);
38502             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38503             h -= this.el.getBorderWidth("tb");
38504             if(this.config.adjustments){
38505                 h += this.config.adjustments[1];
38506             }
38507             this.bodyEl.setHeight(h);
38508             if(this.tabs){
38509                 h = this.tabs.syncHeight(h);
38510             }
38511         }
38512         if(this.panelSize){
38513             w = w !== null ? w : this.panelSize.width;
38514             h = h !== null ? h : this.panelSize.height;
38515         }
38516         if(this.activePanel){
38517             var el = this.activePanel.getEl();
38518             w = w !== null ? w : el.getWidth();
38519             h = h !== null ? h : el.getHeight();
38520             this.panelSize = {width: w, height: h};
38521             this.activePanel.setSize(w, h);
38522         }
38523         if(Roo.isIE && this.tabs){
38524             this.tabs.el.repaint();
38525         }
38526     },
38527
38528     /**
38529      * Returns the container element for this region.
38530      * @return {Roo.Element}
38531      */
38532     getEl : function(){
38533         return this.el;
38534     },
38535
38536     /**
38537      * Hides this region.
38538      */
38539     hide : function(){
38540         //if(!this.collapsed){
38541             this.el.dom.style.left = "-2000px";
38542             this.el.hide();
38543         //}else{
38544          //   this.collapsedEl.dom.style.left = "-2000px";
38545          //   this.collapsedEl.hide();
38546        // }
38547         this.visible = false;
38548         this.fireEvent("visibilitychange", this, false);
38549     },
38550
38551     /**
38552      * Shows this region if it was previously hidden.
38553      */
38554     show : function(){
38555         //if(!this.collapsed){
38556             this.el.show();
38557         //}else{
38558         //    this.collapsedEl.show();
38559        // }
38560         this.visible = true;
38561         this.fireEvent("visibilitychange", this, true);
38562     },
38563 /*
38564     closeClicked : function(){
38565         if(this.activePanel){
38566             this.remove(this.activePanel);
38567         }
38568     },
38569
38570     collapseClick : function(e){
38571         if(this.isSlid){
38572            e.stopPropagation();
38573            this.slideIn();
38574         }else{
38575            e.stopPropagation();
38576            this.slideOut();
38577         }
38578     },
38579 */
38580     /**
38581      * Collapses this region.
38582      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38583      */
38584     /*
38585     collapse : function(skipAnim, skipCheck = false){
38586         if(this.collapsed) {
38587             return;
38588         }
38589         
38590         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38591             
38592             this.collapsed = true;
38593             if(this.split){
38594                 this.split.el.hide();
38595             }
38596             if(this.config.animate && skipAnim !== true){
38597                 this.fireEvent("invalidated", this);
38598                 this.animateCollapse();
38599             }else{
38600                 this.el.setLocation(-20000,-20000);
38601                 this.el.hide();
38602                 this.collapsedEl.show();
38603                 this.fireEvent("collapsed", this);
38604                 this.fireEvent("invalidated", this);
38605             }
38606         }
38607         
38608     },
38609 */
38610     animateCollapse : function(){
38611         // overridden
38612     },
38613
38614     /**
38615      * Expands this region if it was previously collapsed.
38616      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38617      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38618      */
38619     /*
38620     expand : function(e, skipAnim){
38621         if(e) {
38622             e.stopPropagation();
38623         }
38624         if(!this.collapsed || this.el.hasActiveFx()) {
38625             return;
38626         }
38627         if(this.isSlid){
38628             this.afterSlideIn();
38629             skipAnim = true;
38630         }
38631         this.collapsed = false;
38632         if(this.config.animate && skipAnim !== true){
38633             this.animateExpand();
38634         }else{
38635             this.el.show();
38636             if(this.split){
38637                 this.split.el.show();
38638             }
38639             this.collapsedEl.setLocation(-2000,-2000);
38640             this.collapsedEl.hide();
38641             this.fireEvent("invalidated", this);
38642             this.fireEvent("expanded", this);
38643         }
38644     },
38645 */
38646     animateExpand : function(){
38647         // overridden
38648     },
38649
38650     initTabs : function()
38651     {
38652         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38653         
38654         var ts = new Roo.bootstrap.panel.Tabs({
38655             el: this.bodyEl.dom,
38656             region : this,
38657             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38658             disableTooltips: this.config.disableTabTips,
38659             toolbar : this.config.toolbar
38660         });
38661         
38662         if(this.config.hideTabs){
38663             ts.stripWrap.setDisplayed(false);
38664         }
38665         this.tabs = ts;
38666         ts.resizeTabs = this.config.resizeTabs === true;
38667         ts.minTabWidth = this.config.minTabWidth || 40;
38668         ts.maxTabWidth = this.config.maxTabWidth || 250;
38669         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38670         ts.monitorResize = false;
38671         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38672         ts.bodyEl.addClass('roo-layout-tabs-body');
38673         this.panels.each(this.initPanelAsTab, this);
38674     },
38675
38676     initPanelAsTab : function(panel){
38677         var ti = this.tabs.addTab(
38678             panel.getEl().id,
38679             panel.getTitle(),
38680             null,
38681             this.config.closeOnTab && panel.isClosable(),
38682             panel.tpl
38683         );
38684         if(panel.tabTip !== undefined){
38685             ti.setTooltip(panel.tabTip);
38686         }
38687         ti.on("activate", function(){
38688               this.setActivePanel(panel);
38689         }, this);
38690         
38691         if(this.config.closeOnTab){
38692             ti.on("beforeclose", function(t, e){
38693                 e.cancel = true;
38694                 this.remove(panel);
38695             }, this);
38696         }
38697         
38698         panel.tabItem = ti;
38699         
38700         return ti;
38701     },
38702
38703     updatePanelTitle : function(panel, title)
38704     {
38705         if(this.activePanel == panel){
38706             this.updateTitle(title);
38707         }
38708         if(this.tabs){
38709             var ti = this.tabs.getTab(panel.getEl().id);
38710             ti.setText(title);
38711             if(panel.tabTip !== undefined){
38712                 ti.setTooltip(panel.tabTip);
38713             }
38714         }
38715     },
38716
38717     updateTitle : function(title){
38718         if(this.titleTextEl && !this.config.title){
38719             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38720         }
38721     },
38722
38723     setActivePanel : function(panel)
38724     {
38725         panel = this.getPanel(panel);
38726         if(this.activePanel && this.activePanel != panel){
38727             if(this.activePanel.setActiveState(false) === false){
38728                 return;
38729             }
38730         }
38731         this.activePanel = panel;
38732         panel.setActiveState(true);
38733         if(this.panelSize){
38734             panel.setSize(this.panelSize.width, this.panelSize.height);
38735         }
38736         if(this.closeBtn){
38737             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38738         }
38739         this.updateTitle(panel.getTitle());
38740         if(this.tabs){
38741             this.fireEvent("invalidated", this);
38742         }
38743         this.fireEvent("panelactivated", this, panel);
38744     },
38745
38746     /**
38747      * Shows the specified panel.
38748      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38749      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38750      */
38751     showPanel : function(panel)
38752     {
38753         panel = this.getPanel(panel);
38754         if(panel){
38755             if(this.tabs){
38756                 var tab = this.tabs.getTab(panel.getEl().id);
38757                 if(tab.isHidden()){
38758                     this.tabs.unhideTab(tab.id);
38759                 }
38760                 tab.activate();
38761             }else{
38762                 this.setActivePanel(panel);
38763             }
38764         }
38765         return panel;
38766     },
38767
38768     /**
38769      * Get the active panel for this region.
38770      * @return {Roo.ContentPanel} The active panel or null
38771      */
38772     getActivePanel : function(){
38773         return this.activePanel;
38774     },
38775
38776     validateVisibility : function(){
38777         if(this.panels.getCount() < 1){
38778             this.updateTitle("&#160;");
38779             this.closeBtn.hide();
38780             this.hide();
38781         }else{
38782             if(!this.isVisible()){
38783                 this.show();
38784             }
38785         }
38786     },
38787
38788     /**
38789      * Adds the passed ContentPanel(s) to this region.
38790      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38791      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38792      */
38793     add : function(panel)
38794     {
38795         if(arguments.length > 1){
38796             for(var i = 0, len = arguments.length; i < len; i++) {
38797                 this.add(arguments[i]);
38798             }
38799             return null;
38800         }
38801         
38802         // if we have not been rendered yet, then we can not really do much of this..
38803         if (!this.bodyEl) {
38804             this.unrendered_panels.push(panel);
38805             return panel;
38806         }
38807         
38808         
38809         
38810         
38811         if(this.hasPanel(panel)){
38812             this.showPanel(panel);
38813             return panel;
38814         }
38815         panel.setRegion(this);
38816         this.panels.add(panel);
38817        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38818             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38819             // and hide them... ???
38820             this.bodyEl.dom.appendChild(panel.getEl().dom);
38821             if(panel.background !== true){
38822                 this.setActivePanel(panel);
38823             }
38824             this.fireEvent("paneladded", this, panel);
38825             return panel;
38826         }
38827         */
38828         if(!this.tabs){
38829             this.initTabs();
38830         }else{
38831             this.initPanelAsTab(panel);
38832         }
38833         
38834         
38835         if(panel.background !== true){
38836             this.tabs.activate(panel.getEl().id);
38837         }
38838         this.fireEvent("paneladded", this, panel);
38839         return panel;
38840     },
38841
38842     /**
38843      * Hides the tab for the specified panel.
38844      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38845      */
38846     hidePanel : function(panel){
38847         if(this.tabs && (panel = this.getPanel(panel))){
38848             this.tabs.hideTab(panel.getEl().id);
38849         }
38850     },
38851
38852     /**
38853      * Unhides the tab for a previously hidden panel.
38854      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38855      */
38856     unhidePanel : function(panel){
38857         if(this.tabs && (panel = this.getPanel(panel))){
38858             this.tabs.unhideTab(panel.getEl().id);
38859         }
38860     },
38861
38862     clearPanels : function(){
38863         while(this.panels.getCount() > 0){
38864              this.remove(this.panels.first());
38865         }
38866     },
38867
38868     /**
38869      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38870      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38871      * @param {Boolean} preservePanel Overrides the config preservePanel option
38872      * @return {Roo.ContentPanel} The panel that was removed
38873      */
38874     remove : function(panel, preservePanel)
38875     {
38876         panel = this.getPanel(panel);
38877         if(!panel){
38878             return null;
38879         }
38880         var e = {};
38881         this.fireEvent("beforeremove", this, panel, e);
38882         if(e.cancel === true){
38883             return null;
38884         }
38885         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38886         var panelId = panel.getId();
38887         this.panels.removeKey(panelId);
38888         if(preservePanel){
38889             document.body.appendChild(panel.getEl().dom);
38890         }
38891         if(this.tabs){
38892             this.tabs.removeTab(panel.getEl().id);
38893         }else if (!preservePanel){
38894             this.bodyEl.dom.removeChild(panel.getEl().dom);
38895         }
38896         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38897             var p = this.panels.first();
38898             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38899             tempEl.appendChild(p.getEl().dom);
38900             this.bodyEl.update("");
38901             this.bodyEl.dom.appendChild(p.getEl().dom);
38902             tempEl = null;
38903             this.updateTitle(p.getTitle());
38904             this.tabs = null;
38905             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38906             this.setActivePanel(p);
38907         }
38908         panel.setRegion(null);
38909         if(this.activePanel == panel){
38910             this.activePanel = null;
38911         }
38912         if(this.config.autoDestroy !== false && preservePanel !== true){
38913             try{panel.destroy();}catch(e){}
38914         }
38915         this.fireEvent("panelremoved", this, panel);
38916         return panel;
38917     },
38918
38919     /**
38920      * Returns the TabPanel component used by this region
38921      * @return {Roo.TabPanel}
38922      */
38923     getTabs : function(){
38924         return this.tabs;
38925     },
38926
38927     createTool : function(parentEl, className){
38928         var btn = Roo.DomHelper.append(parentEl, {
38929             tag: "div",
38930             cls: "x-layout-tools-button",
38931             children: [ {
38932                 tag: "div",
38933                 cls: "roo-layout-tools-button-inner " + className,
38934                 html: "&#160;"
38935             }]
38936         }, true);
38937         btn.addClassOnOver("roo-layout-tools-button-over");
38938         return btn;
38939     }
38940 });/*
38941  * Based on:
38942  * Ext JS Library 1.1.1
38943  * Copyright(c) 2006-2007, Ext JS, LLC.
38944  *
38945  * Originally Released Under LGPL - original licence link has changed is not relivant.
38946  *
38947  * Fork - LGPL
38948  * <script type="text/javascript">
38949  */
38950  
38951
38952
38953 /**
38954  * @class Roo.SplitLayoutRegion
38955  * @extends Roo.LayoutRegion
38956  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38957  */
38958 Roo.bootstrap.layout.Split = function(config){
38959     this.cursor = config.cursor;
38960     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38961 };
38962
38963 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38964 {
38965     splitTip : "Drag to resize.",
38966     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38967     useSplitTips : false,
38968
38969     applyConfig : function(config){
38970         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38971     },
38972     
38973     onRender : function(ctr,pos) {
38974         
38975         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38976         if(!this.config.split){
38977             return;
38978         }
38979         if(!this.split){
38980             
38981             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38982                             tag: "div",
38983                             id: this.el.id + "-split",
38984                             cls: "roo-layout-split roo-layout-split-"+this.position,
38985                             html: "&#160;"
38986             });
38987             /** The SplitBar for this region 
38988             * @type Roo.SplitBar */
38989             // does not exist yet...
38990             Roo.log([this.position, this.orientation]);
38991             
38992             this.split = new Roo.bootstrap.SplitBar({
38993                 dragElement : splitEl,
38994                 resizingElement: this.el,
38995                 orientation : this.orientation
38996             });
38997             
38998             this.split.on("moved", this.onSplitMove, this);
38999             this.split.useShim = this.config.useShim === true;
39000             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
39001             if(this.useSplitTips){
39002                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
39003             }
39004             //if(config.collapsible){
39005             //    this.split.el.on("dblclick", this.collapse,  this);
39006             //}
39007         }
39008         if(typeof this.config.minSize != "undefined"){
39009             this.split.minSize = this.config.minSize;
39010         }
39011         if(typeof this.config.maxSize != "undefined"){
39012             this.split.maxSize = this.config.maxSize;
39013         }
39014         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
39015             this.hideSplitter();
39016         }
39017         
39018     },
39019
39020     getHMaxSize : function(){
39021          var cmax = this.config.maxSize || 10000;
39022          var center = this.mgr.getRegion("center");
39023          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
39024     },
39025
39026     getVMaxSize : function(){
39027          var cmax = this.config.maxSize || 10000;
39028          var center = this.mgr.getRegion("center");
39029          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
39030     },
39031
39032     onSplitMove : function(split, newSize){
39033         this.fireEvent("resized", this, newSize);
39034     },
39035     
39036     /** 
39037      * Returns the {@link Roo.SplitBar} for this region.
39038      * @return {Roo.SplitBar}
39039      */
39040     getSplitBar : function(){
39041         return this.split;
39042     },
39043     
39044     hide : function(){
39045         this.hideSplitter();
39046         Roo.bootstrap.layout.Split.superclass.hide.call(this);
39047     },
39048
39049     hideSplitter : function(){
39050         if(this.split){
39051             this.split.el.setLocation(-2000,-2000);
39052             this.split.el.hide();
39053         }
39054     },
39055
39056     show : function(){
39057         if(this.split){
39058             this.split.el.show();
39059         }
39060         Roo.bootstrap.layout.Split.superclass.show.call(this);
39061     },
39062     
39063     beforeSlide: function(){
39064         if(Roo.isGecko){// firefox overflow auto bug workaround
39065             this.bodyEl.clip();
39066             if(this.tabs) {
39067                 this.tabs.bodyEl.clip();
39068             }
39069             if(this.activePanel){
39070                 this.activePanel.getEl().clip();
39071                 
39072                 if(this.activePanel.beforeSlide){
39073                     this.activePanel.beforeSlide();
39074                 }
39075             }
39076         }
39077     },
39078     
39079     afterSlide : function(){
39080         if(Roo.isGecko){// firefox overflow auto bug workaround
39081             this.bodyEl.unclip();
39082             if(this.tabs) {
39083                 this.tabs.bodyEl.unclip();
39084             }
39085             if(this.activePanel){
39086                 this.activePanel.getEl().unclip();
39087                 if(this.activePanel.afterSlide){
39088                     this.activePanel.afterSlide();
39089                 }
39090             }
39091         }
39092     },
39093
39094     initAutoHide : function(){
39095         if(this.autoHide !== false){
39096             if(!this.autoHideHd){
39097                 var st = new Roo.util.DelayedTask(this.slideIn, this);
39098                 this.autoHideHd = {
39099                     "mouseout": function(e){
39100                         if(!e.within(this.el, true)){
39101                             st.delay(500);
39102                         }
39103                     },
39104                     "mouseover" : function(e){
39105                         st.cancel();
39106                     },
39107                     scope : this
39108                 };
39109             }
39110             this.el.on(this.autoHideHd);
39111         }
39112     },
39113
39114     clearAutoHide : function(){
39115         if(this.autoHide !== false){
39116             this.el.un("mouseout", this.autoHideHd.mouseout);
39117             this.el.un("mouseover", this.autoHideHd.mouseover);
39118         }
39119     },
39120
39121     clearMonitor : function(){
39122         Roo.get(document).un("click", this.slideInIf, this);
39123     },
39124
39125     // these names are backwards but not changed for compat
39126     slideOut : function(){
39127         if(this.isSlid || this.el.hasActiveFx()){
39128             return;
39129         }
39130         this.isSlid = true;
39131         if(this.collapseBtn){
39132             this.collapseBtn.hide();
39133         }
39134         this.closeBtnState = this.closeBtn.getStyle('display');
39135         this.closeBtn.hide();
39136         if(this.stickBtn){
39137             this.stickBtn.show();
39138         }
39139         this.el.show();
39140         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39141         this.beforeSlide();
39142         this.el.setStyle("z-index", 10001);
39143         this.el.slideIn(this.getSlideAnchor(), {
39144             callback: function(){
39145                 this.afterSlide();
39146                 this.initAutoHide();
39147                 Roo.get(document).on("click", this.slideInIf, this);
39148                 this.fireEvent("slideshow", this);
39149             },
39150             scope: this,
39151             block: true
39152         });
39153     },
39154
39155     afterSlideIn : function(){
39156         this.clearAutoHide();
39157         this.isSlid = false;
39158         this.clearMonitor();
39159         this.el.setStyle("z-index", "");
39160         if(this.collapseBtn){
39161             this.collapseBtn.show();
39162         }
39163         this.closeBtn.setStyle('display', this.closeBtnState);
39164         if(this.stickBtn){
39165             this.stickBtn.hide();
39166         }
39167         this.fireEvent("slidehide", this);
39168     },
39169
39170     slideIn : function(cb){
39171         if(!this.isSlid || this.el.hasActiveFx()){
39172             Roo.callback(cb);
39173             return;
39174         }
39175         this.isSlid = false;
39176         this.beforeSlide();
39177         this.el.slideOut(this.getSlideAnchor(), {
39178             callback: function(){
39179                 this.el.setLeftTop(-10000, -10000);
39180                 this.afterSlide();
39181                 this.afterSlideIn();
39182                 Roo.callback(cb);
39183             },
39184             scope: this,
39185             block: true
39186         });
39187     },
39188     
39189     slideInIf : function(e){
39190         if(!e.within(this.el)){
39191             this.slideIn();
39192         }
39193     },
39194
39195     animateCollapse : function(){
39196         this.beforeSlide();
39197         this.el.setStyle("z-index", 20000);
39198         var anchor = this.getSlideAnchor();
39199         this.el.slideOut(anchor, {
39200             callback : function(){
39201                 this.el.setStyle("z-index", "");
39202                 this.collapsedEl.slideIn(anchor, {duration:.3});
39203                 this.afterSlide();
39204                 this.el.setLocation(-10000,-10000);
39205                 this.el.hide();
39206                 this.fireEvent("collapsed", this);
39207             },
39208             scope: this,
39209             block: true
39210         });
39211     },
39212
39213     animateExpand : function(){
39214         this.beforeSlide();
39215         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39216         this.el.setStyle("z-index", 20000);
39217         this.collapsedEl.hide({
39218             duration:.1
39219         });
39220         this.el.slideIn(this.getSlideAnchor(), {
39221             callback : function(){
39222                 this.el.setStyle("z-index", "");
39223                 this.afterSlide();
39224                 if(this.split){
39225                     this.split.el.show();
39226                 }
39227                 this.fireEvent("invalidated", this);
39228                 this.fireEvent("expanded", this);
39229             },
39230             scope: this,
39231             block: true
39232         });
39233     },
39234
39235     anchors : {
39236         "west" : "left",
39237         "east" : "right",
39238         "north" : "top",
39239         "south" : "bottom"
39240     },
39241
39242     sanchors : {
39243         "west" : "l",
39244         "east" : "r",
39245         "north" : "t",
39246         "south" : "b"
39247     },
39248
39249     canchors : {
39250         "west" : "tl-tr",
39251         "east" : "tr-tl",
39252         "north" : "tl-bl",
39253         "south" : "bl-tl"
39254     },
39255
39256     getAnchor : function(){
39257         return this.anchors[this.position];
39258     },
39259
39260     getCollapseAnchor : function(){
39261         return this.canchors[this.position];
39262     },
39263
39264     getSlideAnchor : function(){
39265         return this.sanchors[this.position];
39266     },
39267
39268     getAlignAdj : function(){
39269         var cm = this.cmargins;
39270         switch(this.position){
39271             case "west":
39272                 return [0, 0];
39273             break;
39274             case "east":
39275                 return [0, 0];
39276             break;
39277             case "north":
39278                 return [0, 0];
39279             break;
39280             case "south":
39281                 return [0, 0];
39282             break;
39283         }
39284     },
39285
39286     getExpandAdj : function(){
39287         var c = this.collapsedEl, cm = this.cmargins;
39288         switch(this.position){
39289             case "west":
39290                 return [-(cm.right+c.getWidth()+cm.left), 0];
39291             break;
39292             case "east":
39293                 return [cm.right+c.getWidth()+cm.left, 0];
39294             break;
39295             case "north":
39296                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39297             break;
39298             case "south":
39299                 return [0, cm.top+cm.bottom+c.getHeight()];
39300             break;
39301         }
39302     }
39303 });/*
39304  * Based on:
39305  * Ext JS Library 1.1.1
39306  * Copyright(c) 2006-2007, Ext JS, LLC.
39307  *
39308  * Originally Released Under LGPL - original licence link has changed is not relivant.
39309  *
39310  * Fork - LGPL
39311  * <script type="text/javascript">
39312  */
39313 /*
39314  * These classes are private internal classes
39315  */
39316 Roo.bootstrap.layout.Center = function(config){
39317     config.region = "center";
39318     Roo.bootstrap.layout.Region.call(this, config);
39319     this.visible = true;
39320     this.minWidth = config.minWidth || 20;
39321     this.minHeight = config.minHeight || 20;
39322 };
39323
39324 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39325     hide : function(){
39326         // center panel can't be hidden
39327     },
39328     
39329     show : function(){
39330         // center panel can't be hidden
39331     },
39332     
39333     getMinWidth: function(){
39334         return this.minWidth;
39335     },
39336     
39337     getMinHeight: function(){
39338         return this.minHeight;
39339     }
39340 });
39341
39342
39343
39344
39345  
39346
39347
39348
39349
39350
39351
39352 Roo.bootstrap.layout.North = function(config)
39353 {
39354     config.region = 'north';
39355     config.cursor = 'n-resize';
39356     
39357     Roo.bootstrap.layout.Split.call(this, config);
39358     
39359     
39360     if(this.split){
39361         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39362         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39363         this.split.el.addClass("roo-layout-split-v");
39364     }
39365     //var size = config.initialSize || config.height;
39366     //if(this.el && typeof size != "undefined"){
39367     //    this.el.setHeight(size);
39368     //}
39369 };
39370 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39371 {
39372     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39373      
39374      
39375     onRender : function(ctr, pos)
39376     {
39377         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39378         var size = this.config.initialSize || this.config.height;
39379         if(this.el && typeof size != "undefined"){
39380             this.el.setHeight(size);
39381         }
39382     
39383     },
39384     
39385     getBox : function(){
39386         if(this.collapsed){
39387             return this.collapsedEl.getBox();
39388         }
39389         var box = this.el.getBox();
39390         if(this.split){
39391             box.height += this.split.el.getHeight();
39392         }
39393         return box;
39394     },
39395     
39396     updateBox : function(box){
39397         if(this.split && !this.collapsed){
39398             box.height -= this.split.el.getHeight();
39399             this.split.el.setLeft(box.x);
39400             this.split.el.setTop(box.y+box.height);
39401             this.split.el.setWidth(box.width);
39402         }
39403         if(this.collapsed){
39404             this.updateBody(box.width, null);
39405         }
39406         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39407     }
39408 });
39409
39410
39411
39412
39413
39414 Roo.bootstrap.layout.South = function(config){
39415     config.region = 'south';
39416     config.cursor = 's-resize';
39417     Roo.bootstrap.layout.Split.call(this, config);
39418     if(this.split){
39419         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39420         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39421         this.split.el.addClass("roo-layout-split-v");
39422     }
39423     
39424 };
39425
39426 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39427     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39428     
39429     onRender : function(ctr, pos)
39430     {
39431         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39432         var size = this.config.initialSize || this.config.height;
39433         if(this.el && typeof size != "undefined"){
39434             this.el.setHeight(size);
39435         }
39436     
39437     },
39438     
39439     getBox : function(){
39440         if(this.collapsed){
39441             return this.collapsedEl.getBox();
39442         }
39443         var box = this.el.getBox();
39444         if(this.split){
39445             var sh = this.split.el.getHeight();
39446             box.height += sh;
39447             box.y -= sh;
39448         }
39449         return box;
39450     },
39451     
39452     updateBox : function(box){
39453         if(this.split && !this.collapsed){
39454             var sh = this.split.el.getHeight();
39455             box.height -= sh;
39456             box.y += sh;
39457             this.split.el.setLeft(box.x);
39458             this.split.el.setTop(box.y-sh);
39459             this.split.el.setWidth(box.width);
39460         }
39461         if(this.collapsed){
39462             this.updateBody(box.width, null);
39463         }
39464         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39465     }
39466 });
39467
39468 Roo.bootstrap.layout.East = function(config){
39469     config.region = "east";
39470     config.cursor = "e-resize";
39471     Roo.bootstrap.layout.Split.call(this, config);
39472     if(this.split){
39473         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39474         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39475         this.split.el.addClass("roo-layout-split-h");
39476     }
39477     
39478 };
39479 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39480     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39481     
39482     onRender : function(ctr, pos)
39483     {
39484         Roo.bootstrap.layout.Split.prototype.onRender.call(this, ctr, pos);
39485         var size = this.config.initialSize || this.config.width;
39486         if(this.el && typeof size != "undefined"){
39487             this.el.setWidth(size);
39488         }
39489     
39490     },
39491     
39492     getBox : function(){
39493         if(this.collapsed){
39494             return this.collapsedEl.getBox();
39495         }
39496         var box = this.el.getBox();
39497         if(this.split){
39498             var sw = this.split.el.getWidth();
39499             box.width += sw;
39500             box.x -= sw;
39501         }
39502         return box;
39503     },
39504
39505     updateBox : function(box){
39506         if(this.split && !this.collapsed){
39507             var sw = this.split.el.getWidth();
39508             box.width -= sw;
39509             this.split.el.setLeft(box.x);
39510             this.split.el.setTop(box.y);
39511             this.split.el.setHeight(box.height);
39512             box.x += sw;
39513         }
39514         if(this.collapsed){
39515             this.updateBody(null, box.height);
39516         }
39517         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39518     }
39519 });
39520
39521 Roo.bootstrap.layout.West = function(config){
39522     config.region = "west";
39523     config.cursor = "w-resize";
39524     
39525     Roo.bootstrap.layout.Split.call(this, config);
39526     if(this.split){
39527         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39528         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39529         this.split.el.addClass("roo-layout-split-h");
39530     }
39531     
39532 };
39533 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39534     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39535     
39536     onRender: function(ctr, pos)
39537     {
39538         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39539         var size = this.config.initialSize || this.config.width;
39540         if(typeof size != "undefined"){
39541             this.el.setWidth(size);
39542         }
39543     },
39544     
39545     getBox : function(){
39546         if(this.collapsed){
39547             return this.collapsedEl.getBox();
39548         }
39549         var box = this.el.getBox();
39550         if (box.width == 0) {
39551             box.width = this.config.width; // kludge?
39552         }
39553         if(this.split){
39554             box.width += this.split.el.getWidth();
39555         }
39556         return box;
39557     },
39558     
39559     updateBox : function(box){
39560         if(this.split && !this.collapsed){
39561             var sw = this.split.el.getWidth();
39562             box.width -= sw;
39563             this.split.el.setLeft(box.x+box.width);
39564             this.split.el.setTop(box.y);
39565             this.split.el.setHeight(box.height);
39566         }
39567         if(this.collapsed){
39568             this.updateBody(null, box.height);
39569         }
39570         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39571     }
39572 });Roo.namespace("Roo.bootstrap.panel");/*
39573  * Based on:
39574  * Ext JS Library 1.1.1
39575  * Copyright(c) 2006-2007, Ext JS, LLC.
39576  *
39577  * Originally Released Under LGPL - original licence link has changed is not relivant.
39578  *
39579  * Fork - LGPL
39580  * <script type="text/javascript">
39581  */
39582 /**
39583  * @class Roo.ContentPanel
39584  * @extends Roo.util.Observable
39585  * A basic ContentPanel element.
39586  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39587  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39588  * @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
39589  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39590  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39591  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39592  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39593  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39594  * @cfg {String} title          The title for this panel
39595  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39596  * @cfg {String} url            Calls {@link #setUrl} with this value
39597  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39598  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39599  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39600  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39601  * @cfg {Boolean} iframe      contents are an iframe - makes showing remote sources/CSS feasible..
39602  * @cfg {Boolean} badges render the badges
39603  * @cfg {String} cls  extra classes to use  
39604  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39605
39606  * @constructor
39607  * Create a new ContentPanel.
39608  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39609  * @param {String/Object} config A string to set only the title or a config object
39610  * @param {String} content (optional) Set the HTML content for this panel
39611  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39612  */
39613 Roo.bootstrap.panel.Content = function( config){
39614     
39615     this.tpl = config.tpl || false;
39616     
39617     var el = config.el;
39618     var content = config.content;
39619
39620     if(config.autoCreate){ // xtype is available if this is called from factory
39621         el = Roo.id();
39622     }
39623     this.el = Roo.get(el);
39624     if(!this.el && config && config.autoCreate){
39625         if(typeof config.autoCreate == "object"){
39626             if(!config.autoCreate.id){
39627                 config.autoCreate.id = config.id||el;
39628             }
39629             this.el = Roo.DomHelper.append(document.body,
39630                         config.autoCreate, true);
39631         }else{
39632             var elcfg =  {
39633                 tag: "div",
39634                 cls: (config.cls || '') +
39635                     (config.background ? ' bg-' + config.background : '') +
39636                     " roo-layout-inactive-content",
39637                 id: config.id||el
39638             };
39639             if (config.iframe) {
39640                 elcfg.cn = [
39641                     {
39642                         tag : 'iframe',
39643                         style : 'border: 0px',
39644                         src : 'about:blank'
39645                     }
39646                 ];
39647             }
39648               
39649             if (config.html) {
39650                 elcfg.html = config.html;
39651                 
39652             }
39653                         
39654             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39655             if (config.iframe) {
39656                 this.iframeEl = this.el.select('iframe',true).first();
39657             }
39658             
39659         }
39660     } 
39661     this.closable = false;
39662     this.loaded = false;
39663     this.active = false;
39664    
39665       
39666     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39667         
39668         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39669         
39670         this.wrapEl = this.el; //this.el.wrap();
39671         var ti = [];
39672         if (config.toolbar.items) {
39673             ti = config.toolbar.items ;
39674             delete config.toolbar.items ;
39675         }
39676         
39677         var nitems = [];
39678         this.toolbar.render(this.wrapEl, 'before');
39679         for(var i =0;i < ti.length;i++) {
39680           //  Roo.log(['add child', items[i]]);
39681             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39682         }
39683         this.toolbar.items = nitems;
39684         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39685         delete config.toolbar;
39686         
39687     }
39688     /*
39689     // xtype created footer. - not sure if will work as we normally have to render first..
39690     if (this.footer && !this.footer.el && this.footer.xtype) {
39691         if (!this.wrapEl) {
39692             this.wrapEl = this.el.wrap();
39693         }
39694     
39695         this.footer.container = this.wrapEl.createChild();
39696          
39697         this.footer = Roo.factory(this.footer, Roo);
39698         
39699     }
39700     */
39701     
39702      if(typeof config == "string"){
39703         this.title = config;
39704     }else{
39705         Roo.apply(this, config);
39706     }
39707     
39708     if(this.resizeEl){
39709         this.resizeEl = Roo.get(this.resizeEl, true);
39710     }else{
39711         this.resizeEl = this.el;
39712     }
39713     // handle view.xtype
39714     
39715  
39716     
39717     
39718     this.addEvents({
39719         /**
39720          * @event activate
39721          * Fires when this panel is activated. 
39722          * @param {Roo.ContentPanel} this
39723          */
39724         "activate" : true,
39725         /**
39726          * @event deactivate
39727          * Fires when this panel is activated. 
39728          * @param {Roo.ContentPanel} this
39729          */
39730         "deactivate" : true,
39731
39732         /**
39733          * @event resize
39734          * Fires when this panel is resized if fitToFrame is true.
39735          * @param {Roo.ContentPanel} this
39736          * @param {Number} width The width after any component adjustments
39737          * @param {Number} height The height after any component adjustments
39738          */
39739         "resize" : true,
39740         
39741          /**
39742          * @event render
39743          * Fires when this tab is created
39744          * @param {Roo.ContentPanel} this
39745          */
39746         "render" : true
39747         
39748         
39749         
39750     });
39751     
39752
39753     
39754     
39755     if(this.autoScroll && !this.iframe){
39756         this.resizeEl.setStyle("overflow", "auto");
39757     } else {
39758         // fix randome scrolling
39759         //this.el.on('scroll', function() {
39760         //    Roo.log('fix random scolling');
39761         //    this.scrollTo('top',0); 
39762         //});
39763     }
39764     content = content || this.content;
39765     if(content){
39766         this.setContent(content);
39767     }
39768     if(config && config.url){
39769         this.setUrl(this.url, this.params, this.loadOnce);
39770     }
39771     
39772     
39773     
39774     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39775     
39776     if (this.view && typeof(this.view.xtype) != 'undefined') {
39777         this.view.el = this.el.appendChild(document.createElement("div"));
39778         this.view = Roo.factory(this.view); 
39779         this.view.render  &&  this.view.render(false, '');  
39780     }
39781     
39782     
39783     this.fireEvent('render', this);
39784 };
39785
39786 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39787     
39788     cls : '',
39789     background : '',
39790     
39791     tabTip : '',
39792     
39793     iframe : false,
39794     iframeEl : false,
39795     
39796     setRegion : function(region){
39797         this.region = region;
39798         this.setActiveClass(region && !this.background);
39799     },
39800     
39801     
39802     setActiveClass: function(state)
39803     {
39804         if(state){
39805            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39806            this.el.setStyle('position','relative');
39807         }else{
39808            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39809            this.el.setStyle('position', 'absolute');
39810         } 
39811     },
39812     
39813     /**
39814      * Returns the toolbar for this Panel if one was configured. 
39815      * @return {Roo.Toolbar} 
39816      */
39817     getToolbar : function(){
39818         return this.toolbar;
39819     },
39820     
39821     setActiveState : function(active)
39822     {
39823         this.active = active;
39824         this.setActiveClass(active);
39825         if(!active){
39826             if(this.fireEvent("deactivate", this) === false){
39827                 return false;
39828             }
39829             return true;
39830         }
39831         this.fireEvent("activate", this);
39832         return true;
39833     },
39834     /**
39835      * Updates this panel's element (not for iframe)
39836      * @param {String} content The new content
39837      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39838     */
39839     setContent : function(content, loadScripts){
39840         if (this.iframe) {
39841             return;
39842         }
39843         
39844         this.el.update(content, loadScripts);
39845     },
39846
39847     ignoreResize : function(w, h){
39848         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39849             return true;
39850         }else{
39851             this.lastSize = {width: w, height: h};
39852             return false;
39853         }
39854     },
39855     /**
39856      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39857      * @return {Roo.UpdateManager} The UpdateManager
39858      */
39859     getUpdateManager : function(){
39860         if (this.iframe) {
39861             return false;
39862         }
39863         return this.el.getUpdateManager();
39864     },
39865      /**
39866      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39867      * Does not work with IFRAME contents
39868      * @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:
39869 <pre><code>
39870 panel.load({
39871     url: "your-url.php",
39872     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39873     callback: yourFunction,
39874     scope: yourObject, //(optional scope)
39875     discardUrl: false,
39876     nocache: false,
39877     text: "Loading...",
39878     timeout: 30,
39879     scripts: false
39880 });
39881 </code></pre>
39882      
39883      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39884      * 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.
39885      * @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}
39886      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39887      * @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.
39888      * @return {Roo.ContentPanel} this
39889      */
39890     load : function(){
39891         
39892         if (this.iframe) {
39893             return this;
39894         }
39895         
39896         var um = this.el.getUpdateManager();
39897         um.update.apply(um, arguments);
39898         return this;
39899     },
39900
39901
39902     /**
39903      * 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.
39904      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39905      * @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)
39906      * @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)
39907      * @return {Roo.UpdateManager|Boolean} The UpdateManager or false if IFRAME
39908      */
39909     setUrl : function(url, params, loadOnce){
39910         if (this.iframe) {
39911             this.iframeEl.dom.src = url;
39912             return false;
39913         }
39914         
39915         if(this.refreshDelegate){
39916             this.removeListener("activate", this.refreshDelegate);
39917         }
39918         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39919         this.on("activate", this.refreshDelegate);
39920         return this.el.getUpdateManager();
39921     },
39922     
39923     _handleRefresh : function(url, params, loadOnce){
39924         if(!loadOnce || !this.loaded){
39925             var updater = this.el.getUpdateManager();
39926             updater.update(url, params, this._setLoaded.createDelegate(this));
39927         }
39928     },
39929     
39930     _setLoaded : function(){
39931         this.loaded = true;
39932     }, 
39933     
39934     /**
39935      * Returns this panel's id
39936      * @return {String} 
39937      */
39938     getId : function(){
39939         return this.el.id;
39940     },
39941     
39942     /** 
39943      * Returns this panel's element - used by regiosn to add.
39944      * @return {Roo.Element} 
39945      */
39946     getEl : function(){
39947         return this.wrapEl || this.el;
39948     },
39949     
39950    
39951     
39952     adjustForComponents : function(width, height)
39953     {
39954         //Roo.log('adjustForComponents ');
39955         if(this.resizeEl != this.el){
39956             width -= this.el.getFrameWidth('lr');
39957             height -= this.el.getFrameWidth('tb');
39958         }
39959         if(this.toolbar){
39960             var te = this.toolbar.getEl();
39961             te.setWidth(width);
39962             height -= te.getHeight();
39963         }
39964         if(this.footer){
39965             var te = this.footer.getEl();
39966             te.setWidth(width);
39967             height -= te.getHeight();
39968         }
39969         
39970         
39971         if(this.adjustments){
39972             width += this.adjustments[0];
39973             height += this.adjustments[1];
39974         }
39975         return {"width": width, "height": height};
39976     },
39977     
39978     setSize : function(width, height){
39979         if(this.fitToFrame && !this.ignoreResize(width, height)){
39980             if(this.fitContainer && this.resizeEl != this.el){
39981                 this.el.setSize(width, height);
39982             }
39983             var size = this.adjustForComponents(width, height);
39984             if (this.iframe) {
39985                 this.iframeEl.setSize(width,height);
39986             }
39987             
39988             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39989             this.fireEvent('resize', this, size.width, size.height);
39990             
39991             
39992         }
39993     },
39994     
39995     /**
39996      * Returns this panel's title
39997      * @return {String} 
39998      */
39999     getTitle : function(){
40000         
40001         if (typeof(this.title) != 'object') {
40002             return this.title;
40003         }
40004         
40005         var t = '';
40006         for (var k in this.title) {
40007             if (!this.title.hasOwnProperty(k)) {
40008                 continue;
40009             }
40010             
40011             if (k.indexOf('-') >= 0) {
40012                 var s = k.split('-');
40013                 for (var i = 0; i<s.length; i++) {
40014                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
40015                 }
40016             } else {
40017                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
40018             }
40019         }
40020         return t;
40021     },
40022     
40023     /**
40024      * Set this panel's title
40025      * @param {String} title
40026      */
40027     setTitle : function(title){
40028         this.title = title;
40029         if(this.region){
40030             this.region.updatePanelTitle(this, title);
40031         }
40032     },
40033     
40034     /**
40035      * Returns true is this panel was configured to be closable
40036      * @return {Boolean} 
40037      */
40038     isClosable : function(){
40039         return this.closable;
40040     },
40041     
40042     beforeSlide : function(){
40043         this.el.clip();
40044         this.resizeEl.clip();
40045     },
40046     
40047     afterSlide : function(){
40048         this.el.unclip();
40049         this.resizeEl.unclip();
40050     },
40051     
40052     /**
40053      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
40054      *   Will fail silently if the {@link #setUrl} method has not been called.
40055      *   This does not activate the panel, just updates its content.
40056      */
40057     refresh : function(){
40058         if(this.refreshDelegate){
40059            this.loaded = false;
40060            this.refreshDelegate();
40061         }
40062     },
40063     
40064     /**
40065      * Destroys this panel
40066      */
40067     destroy : function(){
40068         this.el.removeAllListeners();
40069         var tempEl = document.createElement("span");
40070         tempEl.appendChild(this.el.dom);
40071         tempEl.innerHTML = "";
40072         this.el.remove();
40073         this.el = null;
40074     },
40075     
40076     /**
40077      * form - if the content panel contains a form - this is a reference to it.
40078      * @type {Roo.form.Form}
40079      */
40080     form : false,
40081     /**
40082      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
40083      *    This contains a reference to it.
40084      * @type {Roo.View}
40085      */
40086     view : false,
40087     
40088       /**
40089      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
40090      * <pre><code>
40091
40092 layout.addxtype({
40093        xtype : 'Form',
40094        items: [ .... ]
40095    }
40096 );
40097
40098 </code></pre>
40099      * @param {Object} cfg Xtype definition of item to add.
40100      */
40101     
40102     
40103     getChildContainer: function () {
40104         return this.getEl();
40105     }
40106     
40107     
40108     /*
40109         var  ret = new Roo.factory(cfg);
40110         return ret;
40111         
40112         
40113         // add form..
40114         if (cfg.xtype.match(/^Form$/)) {
40115             
40116             var el;
40117             //if (this.footer) {
40118             //    el = this.footer.container.insertSibling(false, 'before');
40119             //} else {
40120                 el = this.el.createChild();
40121             //}
40122
40123             this.form = new  Roo.form.Form(cfg);
40124             
40125             
40126             if ( this.form.allItems.length) {
40127                 this.form.render(el.dom);
40128             }
40129             return this.form;
40130         }
40131         // should only have one of theses..
40132         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
40133             // views.. should not be just added - used named prop 'view''
40134             
40135             cfg.el = this.el.appendChild(document.createElement("div"));
40136             // factory?
40137             
40138             var ret = new Roo.factory(cfg);
40139              
40140              ret.render && ret.render(false, ''); // render blank..
40141             this.view = ret;
40142             return ret;
40143         }
40144         return false;
40145     }
40146     \*/
40147 });
40148  
40149 /**
40150  * @class Roo.bootstrap.panel.Grid
40151  * @extends Roo.bootstrap.panel.Content
40152  * @constructor
40153  * Create a new GridPanel.
40154  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
40155  * @param {Object} config A the config object
40156   
40157  */
40158
40159
40160
40161 Roo.bootstrap.panel.Grid = function(config)
40162 {
40163     
40164       
40165     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
40166         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
40167
40168     config.el = this.wrapper;
40169     //this.el = this.wrapper;
40170     
40171       if (config.container) {
40172         // ctor'ed from a Border/panel.grid
40173         
40174         
40175         this.wrapper.setStyle("overflow", "hidden");
40176         this.wrapper.addClass('roo-grid-container');
40177
40178     }
40179     
40180     
40181     if(config.toolbar){
40182         var tool_el = this.wrapper.createChild();    
40183         this.toolbar = Roo.factory(config.toolbar);
40184         var ti = [];
40185         if (config.toolbar.items) {
40186             ti = config.toolbar.items ;
40187             delete config.toolbar.items ;
40188         }
40189         
40190         var nitems = [];
40191         this.toolbar.render(tool_el);
40192         for(var i =0;i < ti.length;i++) {
40193           //  Roo.log(['add child', items[i]]);
40194             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
40195         }
40196         this.toolbar.items = nitems;
40197         
40198         delete config.toolbar;
40199     }
40200     
40201     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
40202     config.grid.scrollBody = true;;
40203     config.grid.monitorWindowResize = false; // turn off autosizing
40204     config.grid.autoHeight = false;
40205     config.grid.autoWidth = false;
40206     
40207     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40208     
40209     if (config.background) {
40210         // render grid on panel activation (if panel background)
40211         this.on('activate', function(gp) {
40212             if (!gp.grid.rendered) {
40213                 gp.grid.render(this.wrapper);
40214                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40215             }
40216         });
40217             
40218     } else {
40219         this.grid.render(this.wrapper);
40220         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40221
40222     }
40223     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40224     // ??? needed ??? config.el = this.wrapper;
40225     
40226     
40227     
40228   
40229     // xtype created footer. - not sure if will work as we normally have to render first..
40230     if (this.footer && !this.footer.el && this.footer.xtype) {
40231         
40232         var ctr = this.grid.getView().getFooterPanel(true);
40233         this.footer.dataSource = this.grid.dataSource;
40234         this.footer = Roo.factory(this.footer, Roo);
40235         this.footer.render(ctr);
40236         
40237     }
40238     
40239     
40240     
40241     
40242      
40243 };
40244
40245 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40246     getId : function(){
40247         return this.grid.id;
40248     },
40249     
40250     /**
40251      * Returns the grid for this panel
40252      * @return {Roo.bootstrap.Table} 
40253      */
40254     getGrid : function(){
40255         return this.grid;    
40256     },
40257     
40258     setSize : function(width, height){
40259         if(!this.ignoreResize(width, height)){
40260             var grid = this.grid;
40261             var size = this.adjustForComponents(width, height);
40262             // tfoot is not a footer?
40263           
40264             
40265             var gridel = grid.getGridEl();
40266             gridel.setSize(size.width, size.height);
40267             
40268             var tbd = grid.getGridEl().select('tbody', true).first();
40269             var thd = grid.getGridEl().select('thead',true).first();
40270             var tbf= grid.getGridEl().select('tfoot', true).first();
40271
40272             if (tbf) {
40273                 size.height -= tbf.getHeight();
40274             }
40275             if (thd) {
40276                 size.height -= thd.getHeight();
40277             }
40278             
40279             tbd.setSize(size.width, size.height );
40280             // this is for the account management tab -seems to work there.
40281             var thd = grid.getGridEl().select('thead',true).first();
40282             //if (tbd) {
40283             //    tbd.setSize(size.width, size.height - thd.getHeight());
40284             //}
40285              
40286             grid.autoSize();
40287         }
40288     },
40289      
40290     
40291     
40292     beforeSlide : function(){
40293         this.grid.getView().scroller.clip();
40294     },
40295     
40296     afterSlide : function(){
40297         this.grid.getView().scroller.unclip();
40298     },
40299     
40300     destroy : function(){
40301         this.grid.destroy();
40302         delete this.grid;
40303         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40304     }
40305 });
40306
40307 /**
40308  * @class Roo.bootstrap.panel.Nest
40309  * @extends Roo.bootstrap.panel.Content
40310  * @constructor
40311  * Create a new Panel, that can contain a layout.Border.
40312  * 
40313  * 
40314  * @param {Roo.BorderLayout} layout The layout for this panel
40315  * @param {String/Object} config A string to set only the title or a config object
40316  */
40317 Roo.bootstrap.panel.Nest = function(config)
40318 {
40319     // construct with only one argument..
40320     /* FIXME - implement nicer consturctors
40321     if (layout.layout) {
40322         config = layout;
40323         layout = config.layout;
40324         delete config.layout;
40325     }
40326     if (layout.xtype && !layout.getEl) {
40327         // then layout needs constructing..
40328         layout = Roo.factory(layout, Roo);
40329     }
40330     */
40331     
40332     config.el =  config.layout.getEl();
40333     
40334     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40335     
40336     config.layout.monitorWindowResize = false; // turn off autosizing
40337     this.layout = config.layout;
40338     this.layout.getEl().addClass("roo-layout-nested-layout");
40339     this.layout.parent = this;
40340     
40341     
40342     
40343     
40344 };
40345
40346 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40347
40348     setSize : function(width, height){
40349         if(!this.ignoreResize(width, height)){
40350             var size = this.adjustForComponents(width, height);
40351             var el = this.layout.getEl();
40352             if (size.height < 1) {
40353                 el.setWidth(size.width);   
40354             } else {
40355                 el.setSize(size.width, size.height);
40356             }
40357             var touch = el.dom.offsetWidth;
40358             this.layout.layout();
40359             // ie requires a double layout on the first pass
40360             if(Roo.isIE && !this.initialized){
40361                 this.initialized = true;
40362                 this.layout.layout();
40363             }
40364         }
40365     },
40366     
40367     // activate all subpanels if not currently active..
40368     
40369     setActiveState : function(active){
40370         this.active = active;
40371         this.setActiveClass(active);
40372         
40373         if(!active){
40374             this.fireEvent("deactivate", this);
40375             return;
40376         }
40377         
40378         this.fireEvent("activate", this);
40379         // not sure if this should happen before or after..
40380         if (!this.layout) {
40381             return; // should not happen..
40382         }
40383         var reg = false;
40384         for (var r in this.layout.regions) {
40385             reg = this.layout.getRegion(r);
40386             if (reg.getActivePanel()) {
40387                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40388                 reg.setActivePanel(reg.getActivePanel());
40389                 continue;
40390             }
40391             if (!reg.panels.length) {
40392                 continue;
40393             }
40394             reg.showPanel(reg.getPanel(0));
40395         }
40396         
40397         
40398         
40399         
40400     },
40401     
40402     /**
40403      * Returns the nested BorderLayout for this panel
40404      * @return {Roo.BorderLayout} 
40405      */
40406     getLayout : function(){
40407         return this.layout;
40408     },
40409     
40410      /**
40411      * Adds a xtype elements to the layout of the nested panel
40412      * <pre><code>
40413
40414 panel.addxtype({
40415        xtype : 'ContentPanel',
40416        region: 'west',
40417        items: [ .... ]
40418    }
40419 );
40420
40421 panel.addxtype({
40422         xtype : 'NestedLayoutPanel',
40423         region: 'west',
40424         layout: {
40425            center: { },
40426            west: { }   
40427         },
40428         items : [ ... list of content panels or nested layout panels.. ]
40429    }
40430 );
40431 </code></pre>
40432      * @param {Object} cfg Xtype definition of item to add.
40433      */
40434     addxtype : function(cfg) {
40435         return this.layout.addxtype(cfg);
40436     
40437     }
40438 });/*
40439  * Based on:
40440  * Ext JS Library 1.1.1
40441  * Copyright(c) 2006-2007, Ext JS, LLC.
40442  *
40443  * Originally Released Under LGPL - original licence link has changed is not relivant.
40444  *
40445  * Fork - LGPL
40446  * <script type="text/javascript">
40447  */
40448 /**
40449  * @class Roo.TabPanel
40450  * @extends Roo.util.Observable
40451  * A lightweight tab container.
40452  * <br><br>
40453  * Usage:
40454  * <pre><code>
40455 // basic tabs 1, built from existing content
40456 var tabs = new Roo.TabPanel("tabs1");
40457 tabs.addTab("script", "View Script");
40458 tabs.addTab("markup", "View Markup");
40459 tabs.activate("script");
40460
40461 // more advanced tabs, built from javascript
40462 var jtabs = new Roo.TabPanel("jtabs");
40463 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40464
40465 // set up the UpdateManager
40466 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40467 var updater = tab2.getUpdateManager();
40468 updater.setDefaultUrl("ajax1.htm");
40469 tab2.on('activate', updater.refresh, updater, true);
40470
40471 // Use setUrl for Ajax loading
40472 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40473 tab3.setUrl("ajax2.htm", null, true);
40474
40475 // Disabled tab
40476 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40477 tab4.disable();
40478
40479 jtabs.activate("jtabs-1");
40480  * </code></pre>
40481  * @constructor
40482  * Create a new TabPanel.
40483  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40484  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40485  */
40486 Roo.bootstrap.panel.Tabs = function(config){
40487     /**
40488     * The container element for this TabPanel.
40489     * @type Roo.Element
40490     */
40491     this.el = Roo.get(config.el);
40492     delete config.el;
40493     if(config){
40494         if(typeof config == "boolean"){
40495             this.tabPosition = config ? "bottom" : "top";
40496         }else{
40497             Roo.apply(this, config);
40498         }
40499     }
40500     
40501     if(this.tabPosition == "bottom"){
40502         // if tabs are at the bottom = create the body first.
40503         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40504         this.el.addClass("roo-tabs-bottom");
40505     }
40506     // next create the tabs holders
40507     
40508     if (this.tabPosition == "west"){
40509         
40510         var reg = this.region; // fake it..
40511         while (reg) {
40512             if (!reg.mgr.parent) {
40513                 break;
40514             }
40515             reg = reg.mgr.parent.region;
40516         }
40517         Roo.log("got nest?");
40518         Roo.log(reg);
40519         if (reg.mgr.getRegion('west')) {
40520             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40521             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40522             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40523             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40524             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40525         
40526             
40527         }
40528         
40529         
40530     } else {
40531      
40532         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40533         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40534         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40535         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40536     }
40537     
40538     
40539     if(Roo.isIE){
40540         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40541     }
40542     
40543     // finally - if tabs are at the top, then create the body last..
40544     if(this.tabPosition != "bottom"){
40545         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40546          * @type Roo.Element
40547          */
40548         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40549         this.el.addClass("roo-tabs-top");
40550     }
40551     this.items = [];
40552
40553     this.bodyEl.setStyle("position", "relative");
40554
40555     this.active = null;
40556     this.activateDelegate = this.activate.createDelegate(this);
40557
40558     this.addEvents({
40559         /**
40560          * @event tabchange
40561          * Fires when the active tab changes
40562          * @param {Roo.TabPanel} this
40563          * @param {Roo.TabPanelItem} activePanel The new active tab
40564          */
40565         "tabchange": true,
40566         /**
40567          * @event beforetabchange
40568          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40569          * @param {Roo.TabPanel} this
40570          * @param {Object} e Set cancel to true on this object to cancel the tab change
40571          * @param {Roo.TabPanelItem} tab The tab being changed to
40572          */
40573         "beforetabchange" : true
40574     });
40575
40576     Roo.EventManager.onWindowResize(this.onResize, this);
40577     this.cpad = this.el.getPadding("lr");
40578     this.hiddenCount = 0;
40579
40580
40581     // toolbar on the tabbar support...
40582     if (this.toolbar) {
40583         alert("no toolbar support yet");
40584         this.toolbar  = false;
40585         /*
40586         var tcfg = this.toolbar;
40587         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40588         this.toolbar = new Roo.Toolbar(tcfg);
40589         if (Roo.isSafari) {
40590             var tbl = tcfg.container.child('table', true);
40591             tbl.setAttribute('width', '100%');
40592         }
40593         */
40594         
40595     }
40596    
40597
40598
40599     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40600 };
40601
40602 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40603     /*
40604      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40605      */
40606     tabPosition : "top",
40607     /*
40608      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40609      */
40610     currentTabWidth : 0,
40611     /*
40612      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40613      */
40614     minTabWidth : 40,
40615     /*
40616      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40617      */
40618     maxTabWidth : 250,
40619     /*
40620      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40621      */
40622     preferredTabWidth : 175,
40623     /*
40624      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40625      */
40626     resizeTabs : false,
40627     /*
40628      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40629      */
40630     monitorResize : true,
40631     /*
40632      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40633      */
40634     toolbar : false,  // set by caller..
40635     
40636     region : false, /// set by caller
40637     
40638     disableTooltips : true, // not used yet...
40639
40640     /**
40641      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40642      * @param {String} id The id of the div to use <b>or create</b>
40643      * @param {String} text The text for the tab
40644      * @param {String} content (optional) Content to put in the TabPanelItem body
40645      * @param {Boolean} closable (optional) True to create a close icon on the tab
40646      * @return {Roo.TabPanelItem} The created TabPanelItem
40647      */
40648     addTab : function(id, text, content, closable, tpl)
40649     {
40650         var item = new Roo.bootstrap.panel.TabItem({
40651             panel: this,
40652             id : id,
40653             text : text,
40654             closable : closable,
40655             tpl : tpl
40656         });
40657         this.addTabItem(item);
40658         if(content){
40659             item.setContent(content);
40660         }
40661         return item;
40662     },
40663
40664     /**
40665      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40666      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40667      * @return {Roo.TabPanelItem}
40668      */
40669     getTab : function(id){
40670         return this.items[id];
40671     },
40672
40673     /**
40674      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40675      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40676      */
40677     hideTab : function(id){
40678         var t = this.items[id];
40679         if(!t.isHidden()){
40680            t.setHidden(true);
40681            this.hiddenCount++;
40682            this.autoSizeTabs();
40683         }
40684     },
40685
40686     /**
40687      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40688      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40689      */
40690     unhideTab : function(id){
40691         var t = this.items[id];
40692         if(t.isHidden()){
40693            t.setHidden(false);
40694            this.hiddenCount--;
40695            this.autoSizeTabs();
40696         }
40697     },
40698
40699     /**
40700      * Adds an existing {@link Roo.TabPanelItem}.
40701      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40702      */
40703     addTabItem : function(item)
40704     {
40705         this.items[item.id] = item;
40706         this.items.push(item);
40707         this.autoSizeTabs();
40708       //  if(this.resizeTabs){
40709     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40710   //         this.autoSizeTabs();
40711 //        }else{
40712 //            item.autoSize();
40713        // }
40714     },
40715
40716     /**
40717      * Removes a {@link Roo.TabPanelItem}.
40718      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40719      */
40720     removeTab : function(id){
40721         var items = this.items;
40722         var tab = items[id];
40723         if(!tab) { return; }
40724         var index = items.indexOf(tab);
40725         if(this.active == tab && items.length > 1){
40726             var newTab = this.getNextAvailable(index);
40727             if(newTab) {
40728                 newTab.activate();
40729             }
40730         }
40731         this.stripEl.dom.removeChild(tab.pnode.dom);
40732         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40733             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40734         }
40735         items.splice(index, 1);
40736         delete this.items[tab.id];
40737         tab.fireEvent("close", tab);
40738         tab.purgeListeners();
40739         this.autoSizeTabs();
40740     },
40741
40742     getNextAvailable : function(start){
40743         var items = this.items;
40744         var index = start;
40745         // look for a next tab that will slide over to
40746         // replace the one being removed
40747         while(index < items.length){
40748             var item = items[++index];
40749             if(item && !item.isHidden()){
40750                 return item;
40751             }
40752         }
40753         // if one isn't found select the previous tab (on the left)
40754         index = start;
40755         while(index >= 0){
40756             var item = items[--index];
40757             if(item && !item.isHidden()){
40758                 return item;
40759             }
40760         }
40761         return null;
40762     },
40763
40764     /**
40765      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40766      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40767      */
40768     disableTab : function(id){
40769         var tab = this.items[id];
40770         if(tab && this.active != tab){
40771             tab.disable();
40772         }
40773     },
40774
40775     /**
40776      * Enables a {@link Roo.TabPanelItem} that is disabled.
40777      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40778      */
40779     enableTab : function(id){
40780         var tab = this.items[id];
40781         tab.enable();
40782     },
40783
40784     /**
40785      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40786      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40787      * @return {Roo.TabPanelItem} The TabPanelItem.
40788      */
40789     activate : function(id)
40790     {
40791         //Roo.log('activite:'  + id);
40792         
40793         var tab = this.items[id];
40794         if(!tab){
40795             return null;
40796         }
40797         if(tab == this.active || tab.disabled){
40798             return tab;
40799         }
40800         var e = {};
40801         this.fireEvent("beforetabchange", this, e, tab);
40802         if(e.cancel !== true && !tab.disabled){
40803             if(this.active){
40804                 this.active.hide();
40805             }
40806             this.active = this.items[id];
40807             this.active.show();
40808             this.fireEvent("tabchange", this, this.active);
40809         }
40810         return tab;
40811     },
40812
40813     /**
40814      * Gets the active {@link Roo.TabPanelItem}.
40815      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40816      */
40817     getActiveTab : function(){
40818         return this.active;
40819     },
40820
40821     /**
40822      * Updates the tab body element to fit the height of the container element
40823      * for overflow scrolling
40824      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40825      */
40826     syncHeight : function(targetHeight){
40827         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40828         var bm = this.bodyEl.getMargins();
40829         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40830         this.bodyEl.setHeight(newHeight);
40831         return newHeight;
40832     },
40833
40834     onResize : function(){
40835         if(this.monitorResize){
40836             this.autoSizeTabs();
40837         }
40838     },
40839
40840     /**
40841      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40842      */
40843     beginUpdate : function(){
40844         this.updating = true;
40845     },
40846
40847     /**
40848      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40849      */
40850     endUpdate : function(){
40851         this.updating = false;
40852         this.autoSizeTabs();
40853     },
40854
40855     /**
40856      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40857      */
40858     autoSizeTabs : function()
40859     {
40860         var count = this.items.length;
40861         var vcount = count - this.hiddenCount;
40862         
40863         if (vcount < 2) {
40864             this.stripEl.hide();
40865         } else {
40866             this.stripEl.show();
40867         }
40868         
40869         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40870             return;
40871         }
40872         
40873         
40874         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40875         var availWidth = Math.floor(w / vcount);
40876         var b = this.stripBody;
40877         if(b.getWidth() > w){
40878             var tabs = this.items;
40879             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40880             if(availWidth < this.minTabWidth){
40881                 /*if(!this.sleft){    // incomplete scrolling code
40882                     this.createScrollButtons();
40883                 }
40884                 this.showScroll();
40885                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40886             }
40887         }else{
40888             if(this.currentTabWidth < this.preferredTabWidth){
40889                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40890             }
40891         }
40892     },
40893
40894     /**
40895      * Returns the number of tabs in this TabPanel.
40896      * @return {Number}
40897      */
40898      getCount : function(){
40899          return this.items.length;
40900      },
40901
40902     /**
40903      * Resizes all the tabs to the passed width
40904      * @param {Number} The new width
40905      */
40906     setTabWidth : function(width){
40907         this.currentTabWidth = width;
40908         for(var i = 0, len = this.items.length; i < len; i++) {
40909                 if(!this.items[i].isHidden()) {
40910                 this.items[i].setWidth(width);
40911             }
40912         }
40913     },
40914
40915     /**
40916      * Destroys this TabPanel
40917      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40918      */
40919     destroy : function(removeEl){
40920         Roo.EventManager.removeResizeListener(this.onResize, this);
40921         for(var i = 0, len = this.items.length; i < len; i++){
40922             this.items[i].purgeListeners();
40923         }
40924         if(removeEl === true){
40925             this.el.update("");
40926             this.el.remove();
40927         }
40928     },
40929     
40930     createStrip : function(container)
40931     {
40932         var strip = document.createElement("nav");
40933         strip.className = Roo.bootstrap.version == 4 ?
40934             "navbar-light bg-light" : 
40935             "navbar navbar-default"; //"x-tabs-wrap";
40936         container.appendChild(strip);
40937         return strip;
40938     },
40939     
40940     createStripList : function(strip)
40941     {
40942         // div wrapper for retard IE
40943         // returns the "tr" element.
40944         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40945         //'<div class="x-tabs-strip-wrap">'+
40946           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40947           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40948         return strip.firstChild; //.firstChild.firstChild.firstChild;
40949     },
40950     createBody : function(container)
40951     {
40952         var body = document.createElement("div");
40953         Roo.id(body, "tab-body");
40954         //Roo.fly(body).addClass("x-tabs-body");
40955         Roo.fly(body).addClass("tab-content");
40956         container.appendChild(body);
40957         return body;
40958     },
40959     createItemBody :function(bodyEl, id){
40960         var body = Roo.getDom(id);
40961         if(!body){
40962             body = document.createElement("div");
40963             body.id = id;
40964         }
40965         //Roo.fly(body).addClass("x-tabs-item-body");
40966         Roo.fly(body).addClass("tab-pane");
40967          bodyEl.insertBefore(body, bodyEl.firstChild);
40968         return body;
40969     },
40970     /** @private */
40971     createStripElements :  function(stripEl, text, closable, tpl)
40972     {
40973         var td = document.createElement("li"); // was td..
40974         td.className = 'nav-item';
40975         
40976         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40977         
40978         
40979         stripEl.appendChild(td);
40980         /*if(closable){
40981             td.className = "x-tabs-closable";
40982             if(!this.closeTpl){
40983                 this.closeTpl = new Roo.Template(
40984                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40985                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40986                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40987                 );
40988             }
40989             var el = this.closeTpl.overwrite(td, {"text": text});
40990             var close = el.getElementsByTagName("div")[0];
40991             var inner = el.getElementsByTagName("em")[0];
40992             return {"el": el, "close": close, "inner": inner};
40993         } else {
40994         */
40995         // not sure what this is..
40996 //            if(!this.tabTpl){
40997                 //this.tabTpl = new Roo.Template(
40998                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40999                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
41000                 //);
41001 //                this.tabTpl = new Roo.Template(
41002 //                   '<a href="#">' +
41003 //                   '<span unselectable="on"' +
41004 //                            (this.disableTooltips ? '' : ' title="{text}"') +
41005 //                            ' >{text}</span></a>'
41006 //                );
41007 //                
41008 //            }
41009
41010
41011             var template = tpl || this.tabTpl || false;
41012             
41013             if(!template){
41014                 template =  new Roo.Template(
41015                         Roo.bootstrap.version == 4 ? 
41016                             (
41017                                 '<a class="nav-link" href="#" unselectable="on"' +
41018                                      (this.disableTooltips ? '' : ' title="{text}"') +
41019                                      ' >{text}</a>'
41020                             ) : (
41021                                 '<a class="nav-link" href="#">' +
41022                                 '<span unselectable="on"' +
41023                                          (this.disableTooltips ? '' : ' title="{text}"') +
41024                                     ' >{text}</span></a>'
41025                             )
41026                 );
41027             }
41028             
41029             switch (typeof(template)) {
41030                 case 'object' :
41031                     break;
41032                 case 'string' :
41033                     template = new Roo.Template(template);
41034                     break;
41035                 default :
41036                     break;
41037             }
41038             
41039             var el = template.overwrite(td, {"text": text});
41040             
41041             var inner = el.getElementsByTagName("span")[0];
41042             
41043             return {"el": el, "inner": inner};
41044             
41045     }
41046         
41047     
41048 });
41049
41050 /**
41051  * @class Roo.TabPanelItem
41052  * @extends Roo.util.Observable
41053  * Represents an individual item (tab plus body) in a TabPanel.
41054  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
41055  * @param {String} id The id of this TabPanelItem
41056  * @param {String} text The text for the tab of this TabPanelItem
41057  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
41058  */
41059 Roo.bootstrap.panel.TabItem = function(config){
41060     /**
41061      * The {@link Roo.TabPanel} this TabPanelItem belongs to
41062      * @type Roo.TabPanel
41063      */
41064     this.tabPanel = config.panel;
41065     /**
41066      * The id for this TabPanelItem
41067      * @type String
41068      */
41069     this.id = config.id;
41070     /** @private */
41071     this.disabled = false;
41072     /** @private */
41073     this.text = config.text;
41074     /** @private */
41075     this.loaded = false;
41076     this.closable = config.closable;
41077
41078     /**
41079      * The body element for this TabPanelItem.
41080      * @type Roo.Element
41081      */
41082     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
41083     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
41084     this.bodyEl.setStyle("display", "block");
41085     this.bodyEl.setStyle("zoom", "1");
41086     //this.hideAction();
41087
41088     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
41089     /** @private */
41090     this.el = Roo.get(els.el);
41091     this.inner = Roo.get(els.inner, true);
41092      this.textEl = Roo.bootstrap.version == 4 ?
41093         this.el : Roo.get(this.el.dom.firstChild, true);
41094
41095     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
41096     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
41097
41098     
41099 //    this.el.on("mousedown", this.onTabMouseDown, this);
41100     this.el.on("click", this.onTabClick, this);
41101     /** @private */
41102     if(config.closable){
41103         var c = Roo.get(els.close, true);
41104         c.dom.title = this.closeText;
41105         c.addClassOnOver("close-over");
41106         c.on("click", this.closeClick, this);
41107      }
41108
41109     this.addEvents({
41110          /**
41111          * @event activate
41112          * Fires when this tab becomes the active tab.
41113          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41114          * @param {Roo.TabPanelItem} this
41115          */
41116         "activate": true,
41117         /**
41118          * @event beforeclose
41119          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
41120          * @param {Roo.TabPanelItem} this
41121          * @param {Object} e Set cancel to true on this object to cancel the close.
41122          */
41123         "beforeclose": true,
41124         /**
41125          * @event close
41126          * Fires when this tab is closed.
41127          * @param {Roo.TabPanelItem} this
41128          */
41129          "close": true,
41130         /**
41131          * @event deactivate
41132          * Fires when this tab is no longer the active tab.
41133          * @param {Roo.TabPanel} tabPanel The parent TabPanel
41134          * @param {Roo.TabPanelItem} this
41135          */
41136          "deactivate" : true
41137     });
41138     this.hidden = false;
41139
41140     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
41141 };
41142
41143 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
41144            {
41145     purgeListeners : function(){
41146        Roo.util.Observable.prototype.purgeListeners.call(this);
41147        this.el.removeAllListeners();
41148     },
41149     /**
41150      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
41151      */
41152     show : function(){
41153         this.status_node.addClass("active");
41154         this.showAction();
41155         if(Roo.isOpera){
41156             this.tabPanel.stripWrap.repaint();
41157         }
41158         this.fireEvent("activate", this.tabPanel, this);
41159     },
41160
41161     /**
41162      * Returns true if this tab is the active tab.
41163      * @return {Boolean}
41164      */
41165     isActive : function(){
41166         return this.tabPanel.getActiveTab() == this;
41167     },
41168
41169     /**
41170      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
41171      */
41172     hide : function(){
41173         this.status_node.removeClass("active");
41174         this.hideAction();
41175         this.fireEvent("deactivate", this.tabPanel, this);
41176     },
41177
41178     hideAction : function(){
41179         this.bodyEl.hide();
41180         this.bodyEl.setStyle("position", "absolute");
41181         this.bodyEl.setLeft("-20000px");
41182         this.bodyEl.setTop("-20000px");
41183     },
41184
41185     showAction : function(){
41186         this.bodyEl.setStyle("position", "relative");
41187         this.bodyEl.setTop("");
41188         this.bodyEl.setLeft("");
41189         this.bodyEl.show();
41190     },
41191
41192     /**
41193      * Set the tooltip for the tab.
41194      * @param {String} tooltip The tab's tooltip
41195      */
41196     setTooltip : function(text){
41197         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
41198             this.textEl.dom.qtip = text;
41199             this.textEl.dom.removeAttribute('title');
41200         }else{
41201             this.textEl.dom.title = text;
41202         }
41203     },
41204
41205     onTabClick : function(e){
41206         e.preventDefault();
41207         this.tabPanel.activate(this.id);
41208     },
41209
41210     onTabMouseDown : function(e){
41211         e.preventDefault();
41212         this.tabPanel.activate(this.id);
41213     },
41214 /*
41215     getWidth : function(){
41216         return this.inner.getWidth();
41217     },
41218
41219     setWidth : function(width){
41220         var iwidth = width - this.linode.getPadding("lr");
41221         this.inner.setWidth(iwidth);
41222         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41223         this.linode.setWidth(width);
41224     },
41225 */
41226     /**
41227      * Show or hide the tab
41228      * @param {Boolean} hidden True to hide or false to show.
41229      */
41230     setHidden : function(hidden){
41231         this.hidden = hidden;
41232         this.linode.setStyle("display", hidden ? "none" : "");
41233     },
41234
41235     /**
41236      * Returns true if this tab is "hidden"
41237      * @return {Boolean}
41238      */
41239     isHidden : function(){
41240         return this.hidden;
41241     },
41242
41243     /**
41244      * Returns the text for this tab
41245      * @return {String}
41246      */
41247     getText : function(){
41248         return this.text;
41249     },
41250     /*
41251     autoSize : function(){
41252         //this.el.beginMeasure();
41253         this.textEl.setWidth(1);
41254         /*
41255          *  #2804 [new] Tabs in Roojs
41256          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41257          */
41258         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41259         //this.el.endMeasure();
41260     //},
41261
41262     /**
41263      * Sets the text for the tab (Note: this also sets the tooltip text)
41264      * @param {String} text The tab's text and tooltip
41265      */
41266     setText : function(text){
41267         this.text = text;
41268         this.textEl.update(text);
41269         this.setTooltip(text);
41270         //if(!this.tabPanel.resizeTabs){
41271         //    this.autoSize();
41272         //}
41273     },
41274     /**
41275      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41276      */
41277     activate : function(){
41278         this.tabPanel.activate(this.id);
41279     },
41280
41281     /**
41282      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41283      */
41284     disable : function(){
41285         if(this.tabPanel.active != this){
41286             this.disabled = true;
41287             this.status_node.addClass("disabled");
41288         }
41289     },
41290
41291     /**
41292      * Enables this TabPanelItem if it was previously disabled.
41293      */
41294     enable : function(){
41295         this.disabled = false;
41296         this.status_node.removeClass("disabled");
41297     },
41298
41299     /**
41300      * Sets the content for this TabPanelItem.
41301      * @param {String} content The content
41302      * @param {Boolean} loadScripts true to look for and load scripts
41303      */
41304     setContent : function(content, loadScripts){
41305         this.bodyEl.update(content, loadScripts);
41306     },
41307
41308     /**
41309      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41310      * @return {Roo.UpdateManager} The UpdateManager
41311      */
41312     getUpdateManager : function(){
41313         return this.bodyEl.getUpdateManager();
41314     },
41315
41316     /**
41317      * Set a URL to be used to load the content for this TabPanelItem.
41318      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41319      * @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)
41320      * @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)
41321      * @return {Roo.UpdateManager} The UpdateManager
41322      */
41323     setUrl : function(url, params, loadOnce){
41324         if(this.refreshDelegate){
41325             this.un('activate', this.refreshDelegate);
41326         }
41327         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41328         this.on("activate", this.refreshDelegate);
41329         return this.bodyEl.getUpdateManager();
41330     },
41331
41332     /** @private */
41333     _handleRefresh : function(url, params, loadOnce){
41334         if(!loadOnce || !this.loaded){
41335             var updater = this.bodyEl.getUpdateManager();
41336             updater.update(url, params, this._setLoaded.createDelegate(this));
41337         }
41338     },
41339
41340     /**
41341      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41342      *   Will fail silently if the setUrl method has not been called.
41343      *   This does not activate the panel, just updates its content.
41344      */
41345     refresh : function(){
41346         if(this.refreshDelegate){
41347            this.loaded = false;
41348            this.refreshDelegate();
41349         }
41350     },
41351
41352     /** @private */
41353     _setLoaded : function(){
41354         this.loaded = true;
41355     },
41356
41357     /** @private */
41358     closeClick : function(e){
41359         var o = {};
41360         e.stopEvent();
41361         this.fireEvent("beforeclose", this, o);
41362         if(o.cancel !== true){
41363             this.tabPanel.removeTab(this.id);
41364         }
41365     },
41366     /**
41367      * The text displayed in the tooltip for the close icon.
41368      * @type String
41369      */
41370     closeText : "Close this tab"
41371 });
41372 /**
41373 *    This script refer to:
41374 *    Title: International Telephone Input
41375 *    Author: Jack O'Connor
41376 *    Code version:  v12.1.12
41377 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41378 **/
41379
41380 Roo.bootstrap.PhoneInputData = function() {
41381     var d = [
41382       [
41383         "Afghanistan (‫افغانستان‬‎)",
41384         "af",
41385         "93"
41386       ],
41387       [
41388         "Albania (Shqipëri)",
41389         "al",
41390         "355"
41391       ],
41392       [
41393         "Algeria (‫الجزائر‬‎)",
41394         "dz",
41395         "213"
41396       ],
41397       [
41398         "American Samoa",
41399         "as",
41400         "1684"
41401       ],
41402       [
41403         "Andorra",
41404         "ad",
41405         "376"
41406       ],
41407       [
41408         "Angola",
41409         "ao",
41410         "244"
41411       ],
41412       [
41413         "Anguilla",
41414         "ai",
41415         "1264"
41416       ],
41417       [
41418         "Antigua and Barbuda",
41419         "ag",
41420         "1268"
41421       ],
41422       [
41423         "Argentina",
41424         "ar",
41425         "54"
41426       ],
41427       [
41428         "Armenia (Հայաստան)",
41429         "am",
41430         "374"
41431       ],
41432       [
41433         "Aruba",
41434         "aw",
41435         "297"
41436       ],
41437       [
41438         "Australia",
41439         "au",
41440         "61",
41441         0
41442       ],
41443       [
41444         "Austria (Österreich)",
41445         "at",
41446         "43"
41447       ],
41448       [
41449         "Azerbaijan (Azərbaycan)",
41450         "az",
41451         "994"
41452       ],
41453       [
41454         "Bahamas",
41455         "bs",
41456         "1242"
41457       ],
41458       [
41459         "Bahrain (‫البحرين‬‎)",
41460         "bh",
41461         "973"
41462       ],
41463       [
41464         "Bangladesh (বাংলাদেশ)",
41465         "bd",
41466         "880"
41467       ],
41468       [
41469         "Barbados",
41470         "bb",
41471         "1246"
41472       ],
41473       [
41474         "Belarus (Беларусь)",
41475         "by",
41476         "375"
41477       ],
41478       [
41479         "Belgium (België)",
41480         "be",
41481         "32"
41482       ],
41483       [
41484         "Belize",
41485         "bz",
41486         "501"
41487       ],
41488       [
41489         "Benin (Bénin)",
41490         "bj",
41491         "229"
41492       ],
41493       [
41494         "Bermuda",
41495         "bm",
41496         "1441"
41497       ],
41498       [
41499         "Bhutan (འབྲུག)",
41500         "bt",
41501         "975"
41502       ],
41503       [
41504         "Bolivia",
41505         "bo",
41506         "591"
41507       ],
41508       [
41509         "Bosnia and Herzegovina (Босна и Херцеговина)",
41510         "ba",
41511         "387"
41512       ],
41513       [
41514         "Botswana",
41515         "bw",
41516         "267"
41517       ],
41518       [
41519         "Brazil (Brasil)",
41520         "br",
41521         "55"
41522       ],
41523       [
41524         "British Indian Ocean Territory",
41525         "io",
41526         "246"
41527       ],
41528       [
41529         "British Virgin Islands",
41530         "vg",
41531         "1284"
41532       ],
41533       [
41534         "Brunei",
41535         "bn",
41536         "673"
41537       ],
41538       [
41539         "Bulgaria (България)",
41540         "bg",
41541         "359"
41542       ],
41543       [
41544         "Burkina Faso",
41545         "bf",
41546         "226"
41547       ],
41548       [
41549         "Burundi (Uburundi)",
41550         "bi",
41551         "257"
41552       ],
41553       [
41554         "Cambodia (កម្ពុជា)",
41555         "kh",
41556         "855"
41557       ],
41558       [
41559         "Cameroon (Cameroun)",
41560         "cm",
41561         "237"
41562       ],
41563       [
41564         "Canada",
41565         "ca",
41566         "1",
41567         1,
41568         ["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"]
41569       ],
41570       [
41571         "Cape Verde (Kabu Verdi)",
41572         "cv",
41573         "238"
41574       ],
41575       [
41576         "Caribbean Netherlands",
41577         "bq",
41578         "599",
41579         1
41580       ],
41581       [
41582         "Cayman Islands",
41583         "ky",
41584         "1345"
41585       ],
41586       [
41587         "Central African Republic (République centrafricaine)",
41588         "cf",
41589         "236"
41590       ],
41591       [
41592         "Chad (Tchad)",
41593         "td",
41594         "235"
41595       ],
41596       [
41597         "Chile",
41598         "cl",
41599         "56"
41600       ],
41601       [
41602         "China (中国)",
41603         "cn",
41604         "86"
41605       ],
41606       [
41607         "Christmas Island",
41608         "cx",
41609         "61",
41610         2
41611       ],
41612       [
41613         "Cocos (Keeling) Islands",
41614         "cc",
41615         "61",
41616         1
41617       ],
41618       [
41619         "Colombia",
41620         "co",
41621         "57"
41622       ],
41623       [
41624         "Comoros (‫جزر القمر‬‎)",
41625         "km",
41626         "269"
41627       ],
41628       [
41629         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41630         "cd",
41631         "243"
41632       ],
41633       [
41634         "Congo (Republic) (Congo-Brazzaville)",
41635         "cg",
41636         "242"
41637       ],
41638       [
41639         "Cook Islands",
41640         "ck",
41641         "682"
41642       ],
41643       [
41644         "Costa Rica",
41645         "cr",
41646         "506"
41647       ],
41648       [
41649         "Côte d’Ivoire",
41650         "ci",
41651         "225"
41652       ],
41653       [
41654         "Croatia (Hrvatska)",
41655         "hr",
41656         "385"
41657       ],
41658       [
41659         "Cuba",
41660         "cu",
41661         "53"
41662       ],
41663       [
41664         "Curaçao",
41665         "cw",
41666         "599",
41667         0
41668       ],
41669       [
41670         "Cyprus (Κύπρος)",
41671         "cy",
41672         "357"
41673       ],
41674       [
41675         "Czech Republic (Česká republika)",
41676         "cz",
41677         "420"
41678       ],
41679       [
41680         "Denmark (Danmark)",
41681         "dk",
41682         "45"
41683       ],
41684       [
41685         "Djibouti",
41686         "dj",
41687         "253"
41688       ],
41689       [
41690         "Dominica",
41691         "dm",
41692         "1767"
41693       ],
41694       [
41695         "Dominican Republic (República Dominicana)",
41696         "do",
41697         "1",
41698         2,
41699         ["809", "829", "849"]
41700       ],
41701       [
41702         "Ecuador",
41703         "ec",
41704         "593"
41705       ],
41706       [
41707         "Egypt (‫مصر‬‎)",
41708         "eg",
41709         "20"
41710       ],
41711       [
41712         "El Salvador",
41713         "sv",
41714         "503"
41715       ],
41716       [
41717         "Equatorial Guinea (Guinea Ecuatorial)",
41718         "gq",
41719         "240"
41720       ],
41721       [
41722         "Eritrea",
41723         "er",
41724         "291"
41725       ],
41726       [
41727         "Estonia (Eesti)",
41728         "ee",
41729         "372"
41730       ],
41731       [
41732         "Ethiopia",
41733         "et",
41734         "251"
41735       ],
41736       [
41737         "Falkland Islands (Islas Malvinas)",
41738         "fk",
41739         "500"
41740       ],
41741       [
41742         "Faroe Islands (Føroyar)",
41743         "fo",
41744         "298"
41745       ],
41746       [
41747         "Fiji",
41748         "fj",
41749         "679"
41750       ],
41751       [
41752         "Finland (Suomi)",
41753         "fi",
41754         "358",
41755         0
41756       ],
41757       [
41758         "France",
41759         "fr",
41760         "33"
41761       ],
41762       [
41763         "French Guiana (Guyane française)",
41764         "gf",
41765         "594"
41766       ],
41767       [
41768         "French Polynesia (Polynésie française)",
41769         "pf",
41770         "689"
41771       ],
41772       [
41773         "Gabon",
41774         "ga",
41775         "241"
41776       ],
41777       [
41778         "Gambia",
41779         "gm",
41780         "220"
41781       ],
41782       [
41783         "Georgia (საქართველო)",
41784         "ge",
41785         "995"
41786       ],
41787       [
41788         "Germany (Deutschland)",
41789         "de",
41790         "49"
41791       ],
41792       [
41793         "Ghana (Gaana)",
41794         "gh",
41795         "233"
41796       ],
41797       [
41798         "Gibraltar",
41799         "gi",
41800         "350"
41801       ],
41802       [
41803         "Greece (Ελλάδα)",
41804         "gr",
41805         "30"
41806       ],
41807       [
41808         "Greenland (Kalaallit Nunaat)",
41809         "gl",
41810         "299"
41811       ],
41812       [
41813         "Grenada",
41814         "gd",
41815         "1473"
41816       ],
41817       [
41818         "Guadeloupe",
41819         "gp",
41820         "590",
41821         0
41822       ],
41823       [
41824         "Guam",
41825         "gu",
41826         "1671"
41827       ],
41828       [
41829         "Guatemala",
41830         "gt",
41831         "502"
41832       ],
41833       [
41834         "Guernsey",
41835         "gg",
41836         "44",
41837         1
41838       ],
41839       [
41840         "Guinea (Guinée)",
41841         "gn",
41842         "224"
41843       ],
41844       [
41845         "Guinea-Bissau (Guiné Bissau)",
41846         "gw",
41847         "245"
41848       ],
41849       [
41850         "Guyana",
41851         "gy",
41852         "592"
41853       ],
41854       [
41855         "Haiti",
41856         "ht",
41857         "509"
41858       ],
41859       [
41860         "Honduras",
41861         "hn",
41862         "504"
41863       ],
41864       [
41865         "Hong Kong (香港)",
41866         "hk",
41867         "852"
41868       ],
41869       [
41870         "Hungary (Magyarország)",
41871         "hu",
41872         "36"
41873       ],
41874       [
41875         "Iceland (Ísland)",
41876         "is",
41877         "354"
41878       ],
41879       [
41880         "India (भारत)",
41881         "in",
41882         "91"
41883       ],
41884       [
41885         "Indonesia",
41886         "id",
41887         "62"
41888       ],
41889       [
41890         "Iran (‫ایران‬‎)",
41891         "ir",
41892         "98"
41893       ],
41894       [
41895         "Iraq (‫العراق‬‎)",
41896         "iq",
41897         "964"
41898       ],
41899       [
41900         "Ireland",
41901         "ie",
41902         "353"
41903       ],
41904       [
41905         "Isle of Man",
41906         "im",
41907         "44",
41908         2
41909       ],
41910       [
41911         "Israel (‫ישראל‬‎)",
41912         "il",
41913         "972"
41914       ],
41915       [
41916         "Italy (Italia)",
41917         "it",
41918         "39",
41919         0
41920       ],
41921       [
41922         "Jamaica",
41923         "jm",
41924         "1876"
41925       ],
41926       [
41927         "Japan (日本)",
41928         "jp",
41929         "81"
41930       ],
41931       [
41932         "Jersey",
41933         "je",
41934         "44",
41935         3
41936       ],
41937       [
41938         "Jordan (‫الأردن‬‎)",
41939         "jo",
41940         "962"
41941       ],
41942       [
41943         "Kazakhstan (Казахстан)",
41944         "kz",
41945         "7",
41946         1
41947       ],
41948       [
41949         "Kenya",
41950         "ke",
41951         "254"
41952       ],
41953       [
41954         "Kiribati",
41955         "ki",
41956         "686"
41957       ],
41958       [
41959         "Kosovo",
41960         "xk",
41961         "383"
41962       ],
41963       [
41964         "Kuwait (‫الكويت‬‎)",
41965         "kw",
41966         "965"
41967       ],
41968       [
41969         "Kyrgyzstan (Кыргызстан)",
41970         "kg",
41971         "996"
41972       ],
41973       [
41974         "Laos (ລາວ)",
41975         "la",
41976         "856"
41977       ],
41978       [
41979         "Latvia (Latvija)",
41980         "lv",
41981         "371"
41982       ],
41983       [
41984         "Lebanon (‫لبنان‬‎)",
41985         "lb",
41986         "961"
41987       ],
41988       [
41989         "Lesotho",
41990         "ls",
41991         "266"
41992       ],
41993       [
41994         "Liberia",
41995         "lr",
41996         "231"
41997       ],
41998       [
41999         "Libya (‫ليبيا‬‎)",
42000         "ly",
42001         "218"
42002       ],
42003       [
42004         "Liechtenstein",
42005         "li",
42006         "423"
42007       ],
42008       [
42009         "Lithuania (Lietuva)",
42010         "lt",
42011         "370"
42012       ],
42013       [
42014         "Luxembourg",
42015         "lu",
42016         "352"
42017       ],
42018       [
42019         "Macau (澳門)",
42020         "mo",
42021         "853"
42022       ],
42023       [
42024         "Macedonia (FYROM) (Македонија)",
42025         "mk",
42026         "389"
42027       ],
42028       [
42029         "Madagascar (Madagasikara)",
42030         "mg",
42031         "261"
42032       ],
42033       [
42034         "Malawi",
42035         "mw",
42036         "265"
42037       ],
42038       [
42039         "Malaysia",
42040         "my",
42041         "60"
42042       ],
42043       [
42044         "Maldives",
42045         "mv",
42046         "960"
42047       ],
42048       [
42049         "Mali",
42050         "ml",
42051         "223"
42052       ],
42053       [
42054         "Malta",
42055         "mt",
42056         "356"
42057       ],
42058       [
42059         "Marshall Islands",
42060         "mh",
42061         "692"
42062       ],
42063       [
42064         "Martinique",
42065         "mq",
42066         "596"
42067       ],
42068       [
42069         "Mauritania (‫موريتانيا‬‎)",
42070         "mr",
42071         "222"
42072       ],
42073       [
42074         "Mauritius (Moris)",
42075         "mu",
42076         "230"
42077       ],
42078       [
42079         "Mayotte",
42080         "yt",
42081         "262",
42082         1
42083       ],
42084       [
42085         "Mexico (México)",
42086         "mx",
42087         "52"
42088       ],
42089       [
42090         "Micronesia",
42091         "fm",
42092         "691"
42093       ],
42094       [
42095         "Moldova (Republica Moldova)",
42096         "md",
42097         "373"
42098       ],
42099       [
42100         "Monaco",
42101         "mc",
42102         "377"
42103       ],
42104       [
42105         "Mongolia (Монгол)",
42106         "mn",
42107         "976"
42108       ],
42109       [
42110         "Montenegro (Crna Gora)",
42111         "me",
42112         "382"
42113       ],
42114       [
42115         "Montserrat",
42116         "ms",
42117         "1664"
42118       ],
42119       [
42120         "Morocco (‫المغرب‬‎)",
42121         "ma",
42122         "212",
42123         0
42124       ],
42125       [
42126         "Mozambique (Moçambique)",
42127         "mz",
42128         "258"
42129       ],
42130       [
42131         "Myanmar (Burma) (မြန်မာ)",
42132         "mm",
42133         "95"
42134       ],
42135       [
42136         "Namibia (Namibië)",
42137         "na",
42138         "264"
42139       ],
42140       [
42141         "Nauru",
42142         "nr",
42143         "674"
42144       ],
42145       [
42146         "Nepal (नेपाल)",
42147         "np",
42148         "977"
42149       ],
42150       [
42151         "Netherlands (Nederland)",
42152         "nl",
42153         "31"
42154       ],
42155       [
42156         "New Caledonia (Nouvelle-Calédonie)",
42157         "nc",
42158         "687"
42159       ],
42160       [
42161         "New Zealand",
42162         "nz",
42163         "64"
42164       ],
42165       [
42166         "Nicaragua",
42167         "ni",
42168         "505"
42169       ],
42170       [
42171         "Niger (Nijar)",
42172         "ne",
42173         "227"
42174       ],
42175       [
42176         "Nigeria",
42177         "ng",
42178         "234"
42179       ],
42180       [
42181         "Niue",
42182         "nu",
42183         "683"
42184       ],
42185       [
42186         "Norfolk Island",
42187         "nf",
42188         "672"
42189       ],
42190       [
42191         "North Korea (조선 민주주의 인민 공화국)",
42192         "kp",
42193         "850"
42194       ],
42195       [
42196         "Northern Mariana Islands",
42197         "mp",
42198         "1670"
42199       ],
42200       [
42201         "Norway (Norge)",
42202         "no",
42203         "47",
42204         0
42205       ],
42206       [
42207         "Oman (‫عُمان‬‎)",
42208         "om",
42209         "968"
42210       ],
42211       [
42212         "Pakistan (‫پاکستان‬‎)",
42213         "pk",
42214         "92"
42215       ],
42216       [
42217         "Palau",
42218         "pw",
42219         "680"
42220       ],
42221       [
42222         "Palestine (‫فلسطين‬‎)",
42223         "ps",
42224         "970"
42225       ],
42226       [
42227         "Panama (Panamá)",
42228         "pa",
42229         "507"
42230       ],
42231       [
42232         "Papua New Guinea",
42233         "pg",
42234         "675"
42235       ],
42236       [
42237         "Paraguay",
42238         "py",
42239         "595"
42240       ],
42241       [
42242         "Peru (Perú)",
42243         "pe",
42244         "51"
42245       ],
42246       [
42247         "Philippines",
42248         "ph",
42249         "63"
42250       ],
42251       [
42252         "Poland (Polska)",
42253         "pl",
42254         "48"
42255       ],
42256       [
42257         "Portugal",
42258         "pt",
42259         "351"
42260       ],
42261       [
42262         "Puerto Rico",
42263         "pr",
42264         "1",
42265         3,
42266         ["787", "939"]
42267       ],
42268       [
42269         "Qatar (‫قطر‬‎)",
42270         "qa",
42271         "974"
42272       ],
42273       [
42274         "Réunion (La Réunion)",
42275         "re",
42276         "262",
42277         0
42278       ],
42279       [
42280         "Romania (România)",
42281         "ro",
42282         "40"
42283       ],
42284       [
42285         "Russia (Россия)",
42286         "ru",
42287         "7",
42288         0
42289       ],
42290       [
42291         "Rwanda",
42292         "rw",
42293         "250"
42294       ],
42295       [
42296         "Saint Barthélemy",
42297         "bl",
42298         "590",
42299         1
42300       ],
42301       [
42302         "Saint Helena",
42303         "sh",
42304         "290"
42305       ],
42306       [
42307         "Saint Kitts and Nevis",
42308         "kn",
42309         "1869"
42310       ],
42311       [
42312         "Saint Lucia",
42313         "lc",
42314         "1758"
42315       ],
42316       [
42317         "Saint Martin (Saint-Martin (partie française))",
42318         "mf",
42319         "590",
42320         2
42321       ],
42322       [
42323         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42324         "pm",
42325         "508"
42326       ],
42327       [
42328         "Saint Vincent and the Grenadines",
42329         "vc",
42330         "1784"
42331       ],
42332       [
42333         "Samoa",
42334         "ws",
42335         "685"
42336       ],
42337       [
42338         "San Marino",
42339         "sm",
42340         "378"
42341       ],
42342       [
42343         "São Tomé and Príncipe (São Tomé e Príncipe)",
42344         "st",
42345         "239"
42346       ],
42347       [
42348         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42349         "sa",
42350         "966"
42351       ],
42352       [
42353         "Senegal (Sénégal)",
42354         "sn",
42355         "221"
42356       ],
42357       [
42358         "Serbia (Србија)",
42359         "rs",
42360         "381"
42361       ],
42362       [
42363         "Seychelles",
42364         "sc",
42365         "248"
42366       ],
42367       [
42368         "Sierra Leone",
42369         "sl",
42370         "232"
42371       ],
42372       [
42373         "Singapore",
42374         "sg",
42375         "65"
42376       ],
42377       [
42378         "Sint Maarten",
42379         "sx",
42380         "1721"
42381       ],
42382       [
42383         "Slovakia (Slovensko)",
42384         "sk",
42385         "421"
42386       ],
42387       [
42388         "Slovenia (Slovenija)",
42389         "si",
42390         "386"
42391       ],
42392       [
42393         "Solomon Islands",
42394         "sb",
42395         "677"
42396       ],
42397       [
42398         "Somalia (Soomaaliya)",
42399         "so",
42400         "252"
42401       ],
42402       [
42403         "South Africa",
42404         "za",
42405         "27"
42406       ],
42407       [
42408         "South Korea (대한민국)",
42409         "kr",
42410         "82"
42411       ],
42412       [
42413         "South Sudan (‫جنوب السودان‬‎)",
42414         "ss",
42415         "211"
42416       ],
42417       [
42418         "Spain (España)",
42419         "es",
42420         "34"
42421       ],
42422       [
42423         "Sri Lanka (ශ්‍රී ලංකාව)",
42424         "lk",
42425         "94"
42426       ],
42427       [
42428         "Sudan (‫السودان‬‎)",
42429         "sd",
42430         "249"
42431       ],
42432       [
42433         "Suriname",
42434         "sr",
42435         "597"
42436       ],
42437       [
42438         "Svalbard and Jan Mayen",
42439         "sj",
42440         "47",
42441         1
42442       ],
42443       [
42444         "Swaziland",
42445         "sz",
42446         "268"
42447       ],
42448       [
42449         "Sweden (Sverige)",
42450         "se",
42451         "46"
42452       ],
42453       [
42454         "Switzerland (Schweiz)",
42455         "ch",
42456         "41"
42457       ],
42458       [
42459         "Syria (‫سوريا‬‎)",
42460         "sy",
42461         "963"
42462       ],
42463       [
42464         "Taiwan (台灣)",
42465         "tw",
42466         "886"
42467       ],
42468       [
42469         "Tajikistan",
42470         "tj",
42471         "992"
42472       ],
42473       [
42474         "Tanzania",
42475         "tz",
42476         "255"
42477       ],
42478       [
42479         "Thailand (ไทย)",
42480         "th",
42481         "66"
42482       ],
42483       [
42484         "Timor-Leste",
42485         "tl",
42486         "670"
42487       ],
42488       [
42489         "Togo",
42490         "tg",
42491         "228"
42492       ],
42493       [
42494         "Tokelau",
42495         "tk",
42496         "690"
42497       ],
42498       [
42499         "Tonga",
42500         "to",
42501         "676"
42502       ],
42503       [
42504         "Trinidad and Tobago",
42505         "tt",
42506         "1868"
42507       ],
42508       [
42509         "Tunisia (‫تونس‬‎)",
42510         "tn",
42511         "216"
42512       ],
42513       [
42514         "Turkey (Türkiye)",
42515         "tr",
42516         "90"
42517       ],
42518       [
42519         "Turkmenistan",
42520         "tm",
42521         "993"
42522       ],
42523       [
42524         "Turks and Caicos Islands",
42525         "tc",
42526         "1649"
42527       ],
42528       [
42529         "Tuvalu",
42530         "tv",
42531         "688"
42532       ],
42533       [
42534         "U.S. Virgin Islands",
42535         "vi",
42536         "1340"
42537       ],
42538       [
42539         "Uganda",
42540         "ug",
42541         "256"
42542       ],
42543       [
42544         "Ukraine (Україна)",
42545         "ua",
42546         "380"
42547       ],
42548       [
42549         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42550         "ae",
42551         "971"
42552       ],
42553       [
42554         "United Kingdom",
42555         "gb",
42556         "44",
42557         0
42558       ],
42559       [
42560         "United States",
42561         "us",
42562         "1",
42563         0
42564       ],
42565       [
42566         "Uruguay",
42567         "uy",
42568         "598"
42569       ],
42570       [
42571         "Uzbekistan (Oʻzbekiston)",
42572         "uz",
42573         "998"
42574       ],
42575       [
42576         "Vanuatu",
42577         "vu",
42578         "678"
42579       ],
42580       [
42581         "Vatican City (Città del Vaticano)",
42582         "va",
42583         "39",
42584         1
42585       ],
42586       [
42587         "Venezuela",
42588         "ve",
42589         "58"
42590       ],
42591       [
42592         "Vietnam (Việt Nam)",
42593         "vn",
42594         "84"
42595       ],
42596       [
42597         "Wallis and Futuna (Wallis-et-Futuna)",
42598         "wf",
42599         "681"
42600       ],
42601       [
42602         "Western Sahara (‫الصحراء الغربية‬‎)",
42603         "eh",
42604         "212",
42605         1
42606       ],
42607       [
42608         "Yemen (‫اليمن‬‎)",
42609         "ye",
42610         "967"
42611       ],
42612       [
42613         "Zambia",
42614         "zm",
42615         "260"
42616       ],
42617       [
42618         "Zimbabwe",
42619         "zw",
42620         "263"
42621       ],
42622       [
42623         "Åland Islands",
42624         "ax",
42625         "358",
42626         1
42627       ]
42628   ];
42629   
42630   return d;
42631 }/**
42632 *    This script refer to:
42633 *    Title: International Telephone Input
42634 *    Author: Jack O'Connor
42635 *    Code version:  v12.1.12
42636 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42637 **/
42638
42639 /**
42640  * @class Roo.bootstrap.PhoneInput
42641  * @extends Roo.bootstrap.TriggerField
42642  * An input with International dial-code selection
42643  
42644  * @cfg {String} defaultDialCode default '+852'
42645  * @cfg {Array} preferedCountries default []
42646   
42647  * @constructor
42648  * Create a new PhoneInput.
42649  * @param {Object} config Configuration options
42650  */
42651
42652 Roo.bootstrap.PhoneInput = function(config) {
42653     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42654 };
42655
42656 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42657         
42658         listWidth: undefined,
42659         
42660         selectedClass: 'active',
42661         
42662         invalidClass : "has-warning",
42663         
42664         validClass: 'has-success',
42665         
42666         allowed: '0123456789',
42667         
42668         max_length: 15,
42669         
42670         /**
42671          * @cfg {String} defaultDialCode The default dial code when initializing the input
42672          */
42673         defaultDialCode: '+852',
42674         
42675         /**
42676          * @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
42677          */
42678         preferedCountries: false,
42679         
42680         getAutoCreate : function()
42681         {
42682             var data = Roo.bootstrap.PhoneInputData();
42683             var align = this.labelAlign || this.parentLabelAlign();
42684             var id = Roo.id();
42685             
42686             this.allCountries = [];
42687             this.dialCodeMapping = [];
42688             
42689             for (var i = 0; i < data.length; i++) {
42690               var c = data[i];
42691               this.allCountries[i] = {
42692                 name: c[0],
42693                 iso2: c[1],
42694                 dialCode: c[2],
42695                 priority: c[3] || 0,
42696                 areaCodes: c[4] || null
42697               };
42698               this.dialCodeMapping[c[2]] = {
42699                   name: c[0],
42700                   iso2: c[1],
42701                   priority: c[3] || 0,
42702                   areaCodes: c[4] || null
42703               };
42704             }
42705             
42706             var cfg = {
42707                 cls: 'form-group',
42708                 cn: []
42709             };
42710             
42711             var input =  {
42712                 tag: 'input',
42713                 id : id,
42714                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42715                 maxlength: this.max_length,
42716                 cls : 'form-control tel-input',
42717                 autocomplete: 'new-password'
42718             };
42719             
42720             var hiddenInput = {
42721                 tag: 'input',
42722                 type: 'hidden',
42723                 cls: 'hidden-tel-input'
42724             };
42725             
42726             if (this.name) {
42727                 hiddenInput.name = this.name;
42728             }
42729             
42730             if (this.disabled) {
42731                 input.disabled = true;
42732             }
42733             
42734             var flag_container = {
42735                 tag: 'div',
42736                 cls: 'flag-box',
42737                 cn: [
42738                     {
42739                         tag: 'div',
42740                         cls: 'flag'
42741                     },
42742                     {
42743                         tag: 'div',
42744                         cls: 'caret'
42745                     }
42746                 ]
42747             };
42748             
42749             var box = {
42750                 tag: 'div',
42751                 cls: this.hasFeedback ? 'has-feedback' : '',
42752                 cn: [
42753                     hiddenInput,
42754                     input,
42755                     {
42756                         tag: 'input',
42757                         cls: 'dial-code-holder',
42758                         disabled: true
42759                     }
42760                 ]
42761             };
42762             
42763             var container = {
42764                 cls: 'roo-select2-container input-group',
42765                 cn: [
42766                     flag_container,
42767                     box
42768                 ]
42769             };
42770             
42771             if (this.fieldLabel.length) {
42772                 var indicator = {
42773                     tag: 'i',
42774                     tooltip: 'This field is required'
42775                 };
42776                 
42777                 var label = {
42778                     tag: 'label',
42779                     'for':  id,
42780                     cls: 'control-label',
42781                     cn: []
42782                 };
42783                 
42784                 var label_text = {
42785                     tag: 'span',
42786                     html: this.fieldLabel
42787                 };
42788                 
42789                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42790                 label.cn = [
42791                     indicator,
42792                     label_text
42793                 ];
42794                 
42795                 if(this.indicatorpos == 'right') {
42796                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42797                     label.cn = [
42798                         label_text,
42799                         indicator
42800                     ];
42801                 }
42802                 
42803                 if(align == 'left') {
42804                     container = {
42805                         tag: 'div',
42806                         cn: [
42807                             container
42808                         ]
42809                     };
42810                     
42811                     if(this.labelWidth > 12){
42812                         label.style = "width: " + this.labelWidth + 'px';
42813                     }
42814                     if(this.labelWidth < 13 && this.labelmd == 0){
42815                         this.labelmd = this.labelWidth;
42816                     }
42817                     if(this.labellg > 0){
42818                         label.cls += ' col-lg-' + this.labellg;
42819                         input.cls += ' col-lg-' + (12 - this.labellg);
42820                     }
42821                     if(this.labelmd > 0){
42822                         label.cls += ' col-md-' + this.labelmd;
42823                         container.cls += ' col-md-' + (12 - this.labelmd);
42824                     }
42825                     if(this.labelsm > 0){
42826                         label.cls += ' col-sm-' + this.labelsm;
42827                         container.cls += ' col-sm-' + (12 - this.labelsm);
42828                     }
42829                     if(this.labelxs > 0){
42830                         label.cls += ' col-xs-' + this.labelxs;
42831                         container.cls += ' col-xs-' + (12 - this.labelxs);
42832                     }
42833                 }
42834             }
42835             
42836             cfg.cn = [
42837                 label,
42838                 container
42839             ];
42840             
42841             var settings = this;
42842             
42843             ['xs','sm','md','lg'].map(function(size){
42844                 if (settings[size]) {
42845                     cfg.cls += ' col-' + size + '-' + settings[size];
42846                 }
42847             });
42848             
42849             this.store = new Roo.data.Store({
42850                 proxy : new Roo.data.MemoryProxy({}),
42851                 reader : new Roo.data.JsonReader({
42852                     fields : [
42853                         {
42854                             'name' : 'name',
42855                             'type' : 'string'
42856                         },
42857                         {
42858                             'name' : 'iso2',
42859                             'type' : 'string'
42860                         },
42861                         {
42862                             'name' : 'dialCode',
42863                             'type' : 'string'
42864                         },
42865                         {
42866                             'name' : 'priority',
42867                             'type' : 'string'
42868                         },
42869                         {
42870                             'name' : 'areaCodes',
42871                             'type' : 'string'
42872                         }
42873                     ]
42874                 })
42875             });
42876             
42877             if(!this.preferedCountries) {
42878                 this.preferedCountries = [
42879                     'hk',
42880                     'gb',
42881                     'us'
42882                 ];
42883             }
42884             
42885             var p = this.preferedCountries.reverse();
42886             
42887             if(p) {
42888                 for (var i = 0; i < p.length; i++) {
42889                     for (var j = 0; j < this.allCountries.length; j++) {
42890                         if(this.allCountries[j].iso2 == p[i]) {
42891                             var t = this.allCountries[j];
42892                             this.allCountries.splice(j,1);
42893                             this.allCountries.unshift(t);
42894                         }
42895                     } 
42896                 }
42897             }
42898             
42899             this.store.proxy.data = {
42900                 success: true,
42901                 data: this.allCountries
42902             };
42903             
42904             return cfg;
42905         },
42906         
42907         initEvents : function()
42908         {
42909             this.createList();
42910             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42911             
42912             this.indicator = this.indicatorEl();
42913             this.flag = this.flagEl();
42914             this.dialCodeHolder = this.dialCodeHolderEl();
42915             
42916             this.trigger = this.el.select('div.flag-box',true).first();
42917             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42918             
42919             var _this = this;
42920             
42921             (function(){
42922                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42923                 _this.list.setWidth(lw);
42924             }).defer(100);
42925             
42926             this.list.on('mouseover', this.onViewOver, this);
42927             this.list.on('mousemove', this.onViewMove, this);
42928             this.inputEl().on("keyup", this.onKeyUp, this);
42929             this.inputEl().on("keypress", this.onKeyPress, this);
42930             
42931             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42932
42933             this.view = new Roo.View(this.list, this.tpl, {
42934                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42935             });
42936             
42937             this.view.on('click', this.onViewClick, this);
42938             this.setValue(this.defaultDialCode);
42939         },
42940         
42941         onTriggerClick : function(e)
42942         {
42943             Roo.log('trigger click');
42944             if(this.disabled){
42945                 return;
42946             }
42947             
42948             if(this.isExpanded()){
42949                 this.collapse();
42950                 this.hasFocus = false;
42951             }else {
42952                 this.store.load({});
42953                 this.hasFocus = true;
42954                 this.expand();
42955             }
42956         },
42957         
42958         isExpanded : function()
42959         {
42960             return this.list.isVisible();
42961         },
42962         
42963         collapse : function()
42964         {
42965             if(!this.isExpanded()){
42966                 return;
42967             }
42968             this.list.hide();
42969             Roo.get(document).un('mousedown', this.collapseIf, this);
42970             Roo.get(document).un('mousewheel', this.collapseIf, this);
42971             this.fireEvent('collapse', this);
42972             this.validate();
42973         },
42974         
42975         expand : function()
42976         {
42977             Roo.log('expand');
42978
42979             if(this.isExpanded() || !this.hasFocus){
42980                 return;
42981             }
42982             
42983             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42984             this.list.setWidth(lw);
42985             
42986             this.list.show();
42987             this.restrictHeight();
42988             
42989             Roo.get(document).on('mousedown', this.collapseIf, this);
42990             Roo.get(document).on('mousewheel', this.collapseIf, this);
42991             
42992             this.fireEvent('expand', this);
42993         },
42994         
42995         restrictHeight : function()
42996         {
42997             this.list.alignTo(this.inputEl(), this.listAlign);
42998             this.list.alignTo(this.inputEl(), this.listAlign);
42999         },
43000         
43001         onViewOver : function(e, t)
43002         {
43003             if(this.inKeyMode){
43004                 return;
43005             }
43006             var item = this.view.findItemFromChild(t);
43007             
43008             if(item){
43009                 var index = this.view.indexOf(item);
43010                 this.select(index, false);
43011             }
43012         },
43013
43014         // private
43015         onViewClick : function(view, doFocus, el, e)
43016         {
43017             var index = this.view.getSelectedIndexes()[0];
43018             
43019             var r = this.store.getAt(index);
43020             
43021             if(r){
43022                 this.onSelect(r, index);
43023             }
43024             if(doFocus !== false && !this.blockFocus){
43025                 this.inputEl().focus();
43026             }
43027         },
43028         
43029         onViewMove : function(e, t)
43030         {
43031             this.inKeyMode = false;
43032         },
43033         
43034         select : function(index, scrollIntoView)
43035         {
43036             this.selectedIndex = index;
43037             this.view.select(index);
43038             if(scrollIntoView !== false){
43039                 var el = this.view.getNode(index);
43040                 if(el){
43041                     this.list.scrollChildIntoView(el, false);
43042                 }
43043             }
43044         },
43045         
43046         createList : function()
43047         {
43048             this.list = Roo.get(document.body).createChild({
43049                 tag: 'ul',
43050                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
43051                 style: 'display:none'
43052             });
43053             
43054             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
43055         },
43056         
43057         collapseIf : function(e)
43058         {
43059             var in_combo  = e.within(this.el);
43060             var in_list =  e.within(this.list);
43061             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
43062             
43063             if (in_combo || in_list || is_list) {
43064                 return;
43065             }
43066             this.collapse();
43067         },
43068         
43069         onSelect : function(record, index)
43070         {
43071             if(this.fireEvent('beforeselect', this, record, index) !== false){
43072                 
43073                 this.setFlagClass(record.data.iso2);
43074                 this.setDialCode(record.data.dialCode);
43075                 this.hasFocus = false;
43076                 this.collapse();
43077                 this.fireEvent('select', this, record, index);
43078             }
43079         },
43080         
43081         flagEl : function()
43082         {
43083             var flag = this.el.select('div.flag',true).first();
43084             if(!flag){
43085                 return false;
43086             }
43087             return flag;
43088         },
43089         
43090         dialCodeHolderEl : function()
43091         {
43092             var d = this.el.select('input.dial-code-holder',true).first();
43093             if(!d){
43094                 return false;
43095             }
43096             return d;
43097         },
43098         
43099         setDialCode : function(v)
43100         {
43101             this.dialCodeHolder.dom.value = '+'+v;
43102         },
43103         
43104         setFlagClass : function(n)
43105         {
43106             this.flag.dom.className = 'flag '+n;
43107         },
43108         
43109         getValue : function()
43110         {
43111             var v = this.inputEl().getValue();
43112             if(this.dialCodeHolder) {
43113                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
43114             }
43115             return v;
43116         },
43117         
43118         setValue : function(v)
43119         {
43120             var d = this.getDialCode(v);
43121             
43122             //invalid dial code
43123             if(v.length == 0 || !d || d.length == 0) {
43124                 if(this.rendered){
43125                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
43126                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43127                 }
43128                 return;
43129             }
43130             
43131             //valid dial code
43132             this.setFlagClass(this.dialCodeMapping[d].iso2);
43133             this.setDialCode(d);
43134             this.inputEl().dom.value = v.replace('+'+d,'');
43135             this.hiddenEl().dom.value = this.getValue();
43136             
43137             this.validate();
43138         },
43139         
43140         getDialCode : function(v)
43141         {
43142             v = v ||  '';
43143             
43144             if (v.length == 0) {
43145                 return this.dialCodeHolder.dom.value;
43146             }
43147             
43148             var dialCode = "";
43149             if (v.charAt(0) != "+") {
43150                 return false;
43151             }
43152             var numericChars = "";
43153             for (var i = 1; i < v.length; i++) {
43154               var c = v.charAt(i);
43155               if (!isNaN(c)) {
43156                 numericChars += c;
43157                 if (this.dialCodeMapping[numericChars]) {
43158                   dialCode = v.substr(1, i);
43159                 }
43160                 if (numericChars.length == 4) {
43161                   break;
43162                 }
43163               }
43164             }
43165             return dialCode;
43166         },
43167         
43168         reset : function()
43169         {
43170             this.setValue(this.defaultDialCode);
43171             this.validate();
43172         },
43173         
43174         hiddenEl : function()
43175         {
43176             return this.el.select('input.hidden-tel-input',true).first();
43177         },
43178         
43179         // after setting val
43180         onKeyUp : function(e){
43181             this.setValue(this.getValue());
43182         },
43183         
43184         onKeyPress : function(e){
43185             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
43186                 e.stopEvent();
43187             }
43188         }
43189         
43190 });
43191 /**
43192  * @class Roo.bootstrap.MoneyField
43193  * @extends Roo.bootstrap.ComboBox
43194  * Bootstrap MoneyField class
43195  * 
43196  * @constructor
43197  * Create a new MoneyField.
43198  * @param {Object} config Configuration options
43199  */
43200
43201 Roo.bootstrap.MoneyField = function(config) {
43202     
43203     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
43204     
43205 };
43206
43207 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43208     
43209     /**
43210      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43211      */
43212     allowDecimals : true,
43213     /**
43214      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43215      */
43216     decimalSeparator : ".",
43217     /**
43218      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43219      */
43220     decimalPrecision : 0,
43221     /**
43222      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43223      */
43224     allowNegative : true,
43225     /**
43226      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43227      */
43228     allowZero: true,
43229     /**
43230      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43231      */
43232     minValue : Number.NEGATIVE_INFINITY,
43233     /**
43234      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43235      */
43236     maxValue : Number.MAX_VALUE,
43237     /**
43238      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43239      */
43240     minText : "The minimum value for this field is {0}",
43241     /**
43242      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43243      */
43244     maxText : "The maximum value for this field is {0}",
43245     /**
43246      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43247      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43248      */
43249     nanText : "{0} is not a valid number",
43250     /**
43251      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43252      */
43253     castInt : true,
43254     /**
43255      * @cfg {String} defaults currency of the MoneyField
43256      * value should be in lkey
43257      */
43258     defaultCurrency : false,
43259     /**
43260      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43261      */
43262     thousandsDelimiter : false,
43263     /**
43264      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43265      */
43266     max_length: false,
43267     
43268     inputlg : 9,
43269     inputmd : 9,
43270     inputsm : 9,
43271     inputxs : 6,
43272     
43273     store : false,
43274     
43275     getAutoCreate : function()
43276     {
43277         var align = this.labelAlign || this.parentLabelAlign();
43278         
43279         var id = Roo.id();
43280
43281         var cfg = {
43282             cls: 'form-group',
43283             cn: []
43284         };
43285
43286         var input =  {
43287             tag: 'input',
43288             id : id,
43289             cls : 'form-control roo-money-amount-input',
43290             autocomplete: 'new-password'
43291         };
43292         
43293         var hiddenInput = {
43294             tag: 'input',
43295             type: 'hidden',
43296             id: Roo.id(),
43297             cls: 'hidden-number-input'
43298         };
43299         
43300         if(this.max_length) {
43301             input.maxlength = this.max_length; 
43302         }
43303         
43304         if (this.name) {
43305             hiddenInput.name = this.name;
43306         }
43307
43308         if (this.disabled) {
43309             input.disabled = true;
43310         }
43311
43312         var clg = 12 - this.inputlg;
43313         var cmd = 12 - this.inputmd;
43314         var csm = 12 - this.inputsm;
43315         var cxs = 12 - this.inputxs;
43316         
43317         var container = {
43318             tag : 'div',
43319             cls : 'row roo-money-field',
43320             cn : [
43321                 {
43322                     tag : 'div',
43323                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43324                     cn : [
43325                         {
43326                             tag : 'div',
43327                             cls: 'roo-select2-container input-group',
43328                             cn: [
43329                                 {
43330                                     tag : 'input',
43331                                     cls : 'form-control roo-money-currency-input',
43332                                     autocomplete: 'new-password',
43333                                     readOnly : 1,
43334                                     name : this.currencyName
43335                                 },
43336                                 {
43337                                     tag :'span',
43338                                     cls : 'input-group-addon',
43339                                     cn : [
43340                                         {
43341                                             tag: 'span',
43342                                             cls: 'caret'
43343                                         }
43344                                     ]
43345                                 }
43346                             ]
43347                         }
43348                     ]
43349                 },
43350                 {
43351                     tag : 'div',
43352                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43353                     cn : [
43354                         {
43355                             tag: 'div',
43356                             cls: this.hasFeedback ? 'has-feedback' : '',
43357                             cn: [
43358                                 input
43359                             ]
43360                         }
43361                     ]
43362                 }
43363             ]
43364             
43365         };
43366         
43367         if (this.fieldLabel.length) {
43368             var indicator = {
43369                 tag: 'i',
43370                 tooltip: 'This field is required'
43371             };
43372
43373             var label = {
43374                 tag: 'label',
43375                 'for':  id,
43376                 cls: 'control-label',
43377                 cn: []
43378             };
43379
43380             var label_text = {
43381                 tag: 'span',
43382                 html: this.fieldLabel
43383             };
43384
43385             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43386             label.cn = [
43387                 indicator,
43388                 label_text
43389             ];
43390
43391             if(this.indicatorpos == 'right') {
43392                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43393                 label.cn = [
43394                     label_text,
43395                     indicator
43396                 ];
43397             }
43398
43399             if(align == 'left') {
43400                 container = {
43401                     tag: 'div',
43402                     cn: [
43403                         container
43404                     ]
43405                 };
43406
43407                 if(this.labelWidth > 12){
43408                     label.style = "width: " + this.labelWidth + 'px';
43409                 }
43410                 if(this.labelWidth < 13 && this.labelmd == 0){
43411                     this.labelmd = this.labelWidth;
43412                 }
43413                 if(this.labellg > 0){
43414                     label.cls += ' col-lg-' + this.labellg;
43415                     input.cls += ' col-lg-' + (12 - this.labellg);
43416                 }
43417                 if(this.labelmd > 0){
43418                     label.cls += ' col-md-' + this.labelmd;
43419                     container.cls += ' col-md-' + (12 - this.labelmd);
43420                 }
43421                 if(this.labelsm > 0){
43422                     label.cls += ' col-sm-' + this.labelsm;
43423                     container.cls += ' col-sm-' + (12 - this.labelsm);
43424                 }
43425                 if(this.labelxs > 0){
43426                     label.cls += ' col-xs-' + this.labelxs;
43427                     container.cls += ' col-xs-' + (12 - this.labelxs);
43428                 }
43429             }
43430         }
43431
43432         cfg.cn = [
43433             label,
43434             container,
43435             hiddenInput
43436         ];
43437         
43438         var settings = this;
43439
43440         ['xs','sm','md','lg'].map(function(size){
43441             if (settings[size]) {
43442                 cfg.cls += ' col-' + size + '-' + settings[size];
43443             }
43444         });
43445         
43446         return cfg;
43447     },
43448     
43449     initEvents : function()
43450     {
43451         this.indicator = this.indicatorEl();
43452         
43453         this.initCurrencyEvent();
43454         
43455         this.initNumberEvent();
43456     },
43457     
43458     initCurrencyEvent : function()
43459     {
43460         if (!this.store) {
43461             throw "can not find store for combo";
43462         }
43463         
43464         this.store = Roo.factory(this.store, Roo.data);
43465         this.store.parent = this;
43466         
43467         this.createList();
43468         
43469         this.triggerEl = this.el.select('.input-group-addon', true).first();
43470         
43471         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43472         
43473         var _this = this;
43474         
43475         (function(){
43476             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43477             _this.list.setWidth(lw);
43478         }).defer(100);
43479         
43480         this.list.on('mouseover', this.onViewOver, this);
43481         this.list.on('mousemove', this.onViewMove, this);
43482         this.list.on('scroll', this.onViewScroll, this);
43483         
43484         if(!this.tpl){
43485             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43486         }
43487         
43488         this.view = new Roo.View(this.list, this.tpl, {
43489             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43490         });
43491         
43492         this.view.on('click', this.onViewClick, this);
43493         
43494         this.store.on('beforeload', this.onBeforeLoad, this);
43495         this.store.on('load', this.onLoad, this);
43496         this.store.on('loadexception', this.onLoadException, this);
43497         
43498         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43499             "up" : function(e){
43500                 this.inKeyMode = true;
43501                 this.selectPrev();
43502             },
43503
43504             "down" : function(e){
43505                 if(!this.isExpanded()){
43506                     this.onTriggerClick();
43507                 }else{
43508                     this.inKeyMode = true;
43509                     this.selectNext();
43510                 }
43511             },
43512
43513             "enter" : function(e){
43514                 this.collapse();
43515                 
43516                 if(this.fireEvent("specialkey", this, e)){
43517                     this.onViewClick(false);
43518                 }
43519                 
43520                 return true;
43521             },
43522
43523             "esc" : function(e){
43524                 this.collapse();
43525             },
43526
43527             "tab" : function(e){
43528                 this.collapse();
43529                 
43530                 if(this.fireEvent("specialkey", this, e)){
43531                     this.onViewClick(false);
43532                 }
43533                 
43534                 return true;
43535             },
43536
43537             scope : this,
43538
43539             doRelay : function(foo, bar, hname){
43540                 if(hname == 'down' || this.scope.isExpanded()){
43541                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43542                 }
43543                 return true;
43544             },
43545
43546             forceKeyDown: true
43547         });
43548         
43549         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43550         
43551     },
43552     
43553     initNumberEvent : function(e)
43554     {
43555         this.inputEl().on("keydown" , this.fireKey,  this);
43556         this.inputEl().on("focus", this.onFocus,  this);
43557         this.inputEl().on("blur", this.onBlur,  this);
43558         
43559         this.inputEl().relayEvent('keyup', this);
43560         
43561         if(this.indicator){
43562             this.indicator.addClass('invisible');
43563         }
43564  
43565         this.originalValue = this.getValue();
43566         
43567         if(this.validationEvent == 'keyup'){
43568             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43569             this.inputEl().on('keyup', this.filterValidation, this);
43570         }
43571         else if(this.validationEvent !== false){
43572             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43573         }
43574         
43575         if(this.selectOnFocus){
43576             this.on("focus", this.preFocus, this);
43577             
43578         }
43579         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43580             this.inputEl().on("keypress", this.filterKeys, this);
43581         } else {
43582             this.inputEl().relayEvent('keypress', this);
43583         }
43584         
43585         var allowed = "0123456789";
43586         
43587         if(this.allowDecimals){
43588             allowed += this.decimalSeparator;
43589         }
43590         
43591         if(this.allowNegative){
43592             allowed += "-";
43593         }
43594         
43595         if(this.thousandsDelimiter) {
43596             allowed += ",";
43597         }
43598         
43599         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43600         
43601         var keyPress = function(e){
43602             
43603             var k = e.getKey();
43604             
43605             var c = e.getCharCode();
43606             
43607             if(
43608                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43609                     allowed.indexOf(String.fromCharCode(c)) === -1
43610             ){
43611                 e.stopEvent();
43612                 return;
43613             }
43614             
43615             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43616                 return;
43617             }
43618             
43619             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43620                 e.stopEvent();
43621             }
43622         };
43623         
43624         this.inputEl().on("keypress", keyPress, this);
43625         
43626     },
43627     
43628     onTriggerClick : function(e)
43629     {   
43630         if(this.disabled){
43631             return;
43632         }
43633         
43634         this.page = 0;
43635         this.loadNext = false;
43636         
43637         if(this.isExpanded()){
43638             this.collapse();
43639             return;
43640         }
43641         
43642         this.hasFocus = true;
43643         
43644         if(this.triggerAction == 'all') {
43645             this.doQuery(this.allQuery, true);
43646             return;
43647         }
43648         
43649         this.doQuery(this.getRawValue());
43650     },
43651     
43652     getCurrency : function()
43653     {   
43654         var v = this.currencyEl().getValue();
43655         
43656         return v;
43657     },
43658     
43659     restrictHeight : function()
43660     {
43661         this.list.alignTo(this.currencyEl(), this.listAlign);
43662         this.list.alignTo(this.currencyEl(), this.listAlign);
43663     },
43664     
43665     onViewClick : function(view, doFocus, el, e)
43666     {
43667         var index = this.view.getSelectedIndexes()[0];
43668         
43669         var r = this.store.getAt(index);
43670         
43671         if(r){
43672             this.onSelect(r, index);
43673         }
43674     },
43675     
43676     onSelect : function(record, index){
43677         
43678         if(this.fireEvent('beforeselect', this, record, index) !== false){
43679         
43680             this.setFromCurrencyData(index > -1 ? record.data : false);
43681             
43682             this.collapse();
43683             
43684             this.fireEvent('select', this, record, index);
43685         }
43686     },
43687     
43688     setFromCurrencyData : function(o)
43689     {
43690         var currency = '';
43691         
43692         this.lastCurrency = o;
43693         
43694         if (this.currencyField) {
43695             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43696         } else {
43697             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43698         }
43699         
43700         this.lastSelectionText = currency;
43701         
43702         //setting default currency
43703         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43704             this.setCurrency(this.defaultCurrency);
43705             return;
43706         }
43707         
43708         this.setCurrency(currency);
43709     },
43710     
43711     setFromData : function(o)
43712     {
43713         var c = {};
43714         
43715         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43716         
43717         this.setFromCurrencyData(c);
43718         
43719         var value = '';
43720         
43721         if (this.name) {
43722             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43723         } else {
43724             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43725         }
43726         
43727         this.setValue(value);
43728         
43729     },
43730     
43731     setCurrency : function(v)
43732     {   
43733         this.currencyValue = v;
43734         
43735         if(this.rendered){
43736             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43737             this.validate();
43738         }
43739     },
43740     
43741     setValue : function(v)
43742     {
43743         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43744         
43745         this.value = v;
43746         
43747         if(this.rendered){
43748             
43749             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43750             
43751             this.inputEl().dom.value = (v == '') ? '' :
43752                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43753             
43754             if(!this.allowZero && v === '0') {
43755                 this.hiddenEl().dom.value = '';
43756                 this.inputEl().dom.value = '';
43757             }
43758             
43759             this.validate();
43760         }
43761     },
43762     
43763     getRawValue : function()
43764     {
43765         var v = this.inputEl().getValue();
43766         
43767         return v;
43768     },
43769     
43770     getValue : function()
43771     {
43772         return this.fixPrecision(this.parseValue(this.getRawValue()));
43773     },
43774     
43775     parseValue : function(value)
43776     {
43777         if(this.thousandsDelimiter) {
43778             value += "";
43779             r = new RegExp(",", "g");
43780             value = value.replace(r, "");
43781         }
43782         
43783         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43784         return isNaN(value) ? '' : value;
43785         
43786     },
43787     
43788     fixPrecision : function(value)
43789     {
43790         if(this.thousandsDelimiter) {
43791             value += "";
43792             r = new RegExp(",", "g");
43793             value = value.replace(r, "");
43794         }
43795         
43796         var nan = isNaN(value);
43797         
43798         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43799             return nan ? '' : value;
43800         }
43801         return parseFloat(value).toFixed(this.decimalPrecision);
43802     },
43803     
43804     decimalPrecisionFcn : function(v)
43805     {
43806         return Math.floor(v);
43807     },
43808     
43809     validateValue : function(value)
43810     {
43811         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43812             return false;
43813         }
43814         
43815         var num = this.parseValue(value);
43816         
43817         if(isNaN(num)){
43818             this.markInvalid(String.format(this.nanText, value));
43819             return false;
43820         }
43821         
43822         if(num < this.minValue){
43823             this.markInvalid(String.format(this.minText, this.minValue));
43824             return false;
43825         }
43826         
43827         if(num > this.maxValue){
43828             this.markInvalid(String.format(this.maxText, this.maxValue));
43829             return false;
43830         }
43831         
43832         return true;
43833     },
43834     
43835     validate : function()
43836     {
43837         if(this.disabled || this.allowBlank){
43838             this.markValid();
43839             return true;
43840         }
43841         
43842         var currency = this.getCurrency();
43843         
43844         if(this.validateValue(this.getRawValue()) && currency.length){
43845             this.markValid();
43846             return true;
43847         }
43848         
43849         this.markInvalid();
43850         return false;
43851     },
43852     
43853     getName: function()
43854     {
43855         return this.name;
43856     },
43857     
43858     beforeBlur : function()
43859     {
43860         if(!this.castInt){
43861             return;
43862         }
43863         
43864         var v = this.parseValue(this.getRawValue());
43865         
43866         if(v || v == 0){
43867             this.setValue(v);
43868         }
43869     },
43870     
43871     onBlur : function()
43872     {
43873         this.beforeBlur();
43874         
43875         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43876             //this.el.removeClass(this.focusClass);
43877         }
43878         
43879         this.hasFocus = false;
43880         
43881         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43882             this.validate();
43883         }
43884         
43885         var v = this.getValue();
43886         
43887         if(String(v) !== String(this.startValue)){
43888             this.fireEvent('change', this, v, this.startValue);
43889         }
43890         
43891         this.fireEvent("blur", this);
43892     },
43893     
43894     inputEl : function()
43895     {
43896         return this.el.select('.roo-money-amount-input', true).first();
43897     },
43898     
43899     currencyEl : function()
43900     {
43901         return this.el.select('.roo-money-currency-input', true).first();
43902     },
43903     
43904     hiddenEl : function()
43905     {
43906         return this.el.select('input.hidden-number-input',true).first();
43907     }
43908     
43909 });/**
43910  * @class Roo.bootstrap.BezierSignature
43911  * @extends Roo.bootstrap.Component
43912  * Bootstrap BezierSignature class
43913  * This script refer to:
43914  *    Title: Signature Pad
43915  *    Author: szimek
43916  *    Availability: https://github.com/szimek/signature_pad
43917  *
43918  * @constructor
43919  * Create a new BezierSignature
43920  * @param {Object} config The config object
43921  */
43922
43923 Roo.bootstrap.BezierSignature = function(config){
43924     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43925     this.addEvents({
43926         "resize" : true
43927     });
43928 };
43929
43930 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43931 {
43932      
43933     curve_data: [],
43934     
43935     is_empty: true,
43936     
43937     mouse_btn_down: true,
43938     
43939     /**
43940      * @cfg {int} canvas height
43941      */
43942     canvas_height: '200px',
43943     
43944     /**
43945      * @cfg {float|function} Radius of a single dot.
43946      */ 
43947     dot_size: false,
43948     
43949     /**
43950      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43951      */
43952     min_width: 0.5,
43953     
43954     /**
43955      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43956      */
43957     max_width: 2.5,
43958     
43959     /**
43960      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43961      */
43962     throttle: 16,
43963     
43964     /**
43965      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43966      */
43967     min_distance: 5,
43968     
43969     /**
43970      * @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.
43971      */
43972     bg_color: 'rgba(0, 0, 0, 0)',
43973     
43974     /**
43975      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43976      */
43977     dot_color: 'black',
43978     
43979     /**
43980      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43981      */ 
43982     velocity_filter_weight: 0.7,
43983     
43984     /**
43985      * @cfg {function} Callback when stroke begin. 
43986      */
43987     onBegin: false,
43988     
43989     /**
43990      * @cfg {function} Callback when stroke end.
43991      */
43992     onEnd: false,
43993     
43994     getAutoCreate : function()
43995     {
43996         var cls = 'roo-signature column';
43997         
43998         if(this.cls){
43999             cls += ' ' + this.cls;
44000         }
44001         
44002         var col_sizes = [
44003             'lg',
44004             'md',
44005             'sm',
44006             'xs'
44007         ];
44008         
44009         for(var i = 0; i < col_sizes.length; i++) {
44010             if(this[col_sizes[i]]) {
44011                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
44012             }
44013         }
44014         
44015         var cfg = {
44016             tag: 'div',
44017             cls: cls,
44018             cn: [
44019                 {
44020                     tag: 'div',
44021                     cls: 'roo-signature-body',
44022                     cn: [
44023                         {
44024                             tag: 'canvas',
44025                             cls: 'roo-signature-body-canvas',
44026                             height: this.canvas_height,
44027                             width: this.canvas_width
44028                         }
44029                     ]
44030                 },
44031                 {
44032                     tag: 'input',
44033                     type: 'file',
44034                     style: 'display: none'
44035                 }
44036             ]
44037         };
44038         
44039         return cfg;
44040     },
44041     
44042     initEvents: function() 
44043     {
44044         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
44045         
44046         var canvas = this.canvasEl();
44047         
44048         // mouse && touch event swapping...
44049         canvas.dom.style.touchAction = 'none';
44050         canvas.dom.style.msTouchAction = 'none';
44051         
44052         this.mouse_btn_down = false;
44053         canvas.on('mousedown', this._handleMouseDown, this);
44054         canvas.on('mousemove', this._handleMouseMove, this);
44055         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
44056         
44057         if (window.PointerEvent) {
44058             canvas.on('pointerdown', this._handleMouseDown, this);
44059             canvas.on('pointermove', this._handleMouseMove, this);
44060             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
44061         }
44062         
44063         if ('ontouchstart' in window) {
44064             canvas.on('touchstart', this._handleTouchStart, this);
44065             canvas.on('touchmove', this._handleTouchMove, this);
44066             canvas.on('touchend', this._handleTouchEnd, this);
44067         }
44068         
44069         Roo.EventManager.onWindowResize(this.resize, this, true);
44070         
44071         // file input event
44072         this.fileEl().on('change', this.uploadImage, this);
44073         
44074         this.clear();
44075         
44076         this.resize();
44077     },
44078     
44079     resize: function(){
44080         
44081         var canvas = this.canvasEl().dom;
44082         var ctx = this.canvasElCtx();
44083         var img_data = false;
44084         
44085         if(canvas.width > 0) {
44086             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
44087         }
44088         // setting canvas width will clean img data
44089         canvas.width = 0;
44090         
44091         var style = window.getComputedStyle ? 
44092             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
44093             
44094         var padding_left = parseInt(style.paddingLeft) || 0;
44095         var padding_right = parseInt(style.paddingRight) || 0;
44096         
44097         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
44098         
44099         if(img_data) {
44100             ctx.putImageData(img_data, 0, 0);
44101         }
44102     },
44103     
44104     _handleMouseDown: function(e)
44105     {
44106         if (e.browserEvent.which === 1) {
44107             this.mouse_btn_down = true;
44108             this.strokeBegin(e);
44109         }
44110     },
44111     
44112     _handleMouseMove: function (e)
44113     {
44114         if (this.mouse_btn_down) {
44115             this.strokeMoveUpdate(e);
44116         }
44117     },
44118     
44119     _handleMouseUp: function (e)
44120     {
44121         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
44122             this.mouse_btn_down = false;
44123             this.strokeEnd(e);
44124         }
44125     },
44126     
44127     _handleTouchStart: function (e) {
44128         
44129         e.preventDefault();
44130         if (e.browserEvent.targetTouches.length === 1) {
44131             // var touch = e.browserEvent.changedTouches[0];
44132             // this.strokeBegin(touch);
44133             
44134              this.strokeBegin(e); // assume e catching the correct xy...
44135         }
44136     },
44137     
44138     _handleTouchMove: function (e) {
44139         e.preventDefault();
44140         // var touch = event.targetTouches[0];
44141         // _this._strokeMoveUpdate(touch);
44142         this.strokeMoveUpdate(e);
44143     },
44144     
44145     _handleTouchEnd: function (e) {
44146         var wasCanvasTouched = e.target === this.canvasEl().dom;
44147         if (wasCanvasTouched) {
44148             e.preventDefault();
44149             // var touch = event.changedTouches[0];
44150             // _this._strokeEnd(touch);
44151             this.strokeEnd(e);
44152         }
44153     },
44154     
44155     reset: function () {
44156         this._lastPoints = [];
44157         this._lastVelocity = 0;
44158         this._lastWidth = (this.min_width + this.max_width) / 2;
44159         this.canvasElCtx().fillStyle = this.dot_color;
44160     },
44161     
44162     strokeMoveUpdate: function(e)
44163     {
44164         this.strokeUpdate(e);
44165         
44166         if (this.throttle) {
44167             this.throttleStroke(this.strokeUpdate, this.throttle);
44168         }
44169         else {
44170             this.strokeUpdate(e);
44171         }
44172     },
44173     
44174     strokeBegin: function(e)
44175     {
44176         var newPointGroup = {
44177             color: this.dot_color,
44178             points: []
44179         };
44180         
44181         if (typeof this.onBegin === 'function') {
44182             this.onBegin(e);
44183         }
44184         
44185         this.curve_data.push(newPointGroup);
44186         this.reset();
44187         this.strokeUpdate(e);
44188     },
44189     
44190     strokeUpdate: function(e)
44191     {
44192         var rect = this.canvasEl().dom.getBoundingClientRect();
44193         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
44194         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
44195         var lastPoints = lastPointGroup.points;
44196         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
44197         var isLastPointTooClose = lastPoint
44198             ? point.distanceTo(lastPoint) <= this.min_distance
44199             : false;
44200         var color = lastPointGroup.color;
44201         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
44202             var curve = this.addPoint(point);
44203             if (!lastPoint) {
44204                 this.drawDot({color: color, point: point});
44205             }
44206             else if (curve) {
44207                 this.drawCurve({color: color, curve: curve});
44208             }
44209             lastPoints.push({
44210                 time: point.time,
44211                 x: point.x,
44212                 y: point.y
44213             });
44214         }
44215     },
44216     
44217     strokeEnd: function(e)
44218     {
44219         this.strokeUpdate(e);
44220         if (typeof this.onEnd === 'function') {
44221             this.onEnd(e);
44222         }
44223     },
44224     
44225     addPoint:  function (point) {
44226         var _lastPoints = this._lastPoints;
44227         _lastPoints.push(point);
44228         if (_lastPoints.length > 2) {
44229             if (_lastPoints.length === 3) {
44230                 _lastPoints.unshift(_lastPoints[0]);
44231             }
44232             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44233             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44234             _lastPoints.shift();
44235             return curve;
44236         }
44237         return null;
44238     },
44239     
44240     calculateCurveWidths: function (startPoint, endPoint) {
44241         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44242             (1 - this.velocity_filter_weight) * this._lastVelocity;
44243
44244         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44245         var widths = {
44246             end: newWidth,
44247             start: this._lastWidth
44248         };
44249         
44250         this._lastVelocity = velocity;
44251         this._lastWidth = newWidth;
44252         return widths;
44253     },
44254     
44255     drawDot: function (_a) {
44256         var color = _a.color, point = _a.point;
44257         var ctx = this.canvasElCtx();
44258         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44259         ctx.beginPath();
44260         this.drawCurveSegment(point.x, point.y, width);
44261         ctx.closePath();
44262         ctx.fillStyle = color;
44263         ctx.fill();
44264     },
44265     
44266     drawCurve: function (_a) {
44267         var color = _a.color, curve = _a.curve;
44268         var ctx = this.canvasElCtx();
44269         var widthDelta = curve.endWidth - curve.startWidth;
44270         var drawSteps = Math.floor(curve.length()) * 2;
44271         ctx.beginPath();
44272         ctx.fillStyle = color;
44273         for (var i = 0; i < drawSteps; i += 1) {
44274         var t = i / drawSteps;
44275         var tt = t * t;
44276         var ttt = tt * t;
44277         var u = 1 - t;
44278         var uu = u * u;
44279         var uuu = uu * u;
44280         var x = uuu * curve.startPoint.x;
44281         x += 3 * uu * t * curve.control1.x;
44282         x += 3 * u * tt * curve.control2.x;
44283         x += ttt * curve.endPoint.x;
44284         var y = uuu * curve.startPoint.y;
44285         y += 3 * uu * t * curve.control1.y;
44286         y += 3 * u * tt * curve.control2.y;
44287         y += ttt * curve.endPoint.y;
44288         var width = curve.startWidth + ttt * widthDelta;
44289         this.drawCurveSegment(x, y, width);
44290         }
44291         ctx.closePath();
44292         ctx.fill();
44293     },
44294     
44295     drawCurveSegment: function (x, y, width) {
44296         var ctx = this.canvasElCtx();
44297         ctx.moveTo(x, y);
44298         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44299         this.is_empty = false;
44300     },
44301     
44302     clear: function()
44303     {
44304         var ctx = this.canvasElCtx();
44305         var canvas = this.canvasEl().dom;
44306         ctx.fillStyle = this.bg_color;
44307         ctx.clearRect(0, 0, canvas.width, canvas.height);
44308         ctx.fillRect(0, 0, canvas.width, canvas.height);
44309         this.curve_data = [];
44310         this.reset();
44311         this.is_empty = true;
44312     },
44313     
44314     fileEl: function()
44315     {
44316         return  this.el.select('input',true).first();
44317     },
44318     
44319     canvasEl: function()
44320     {
44321         return this.el.select('canvas',true).first();
44322     },
44323     
44324     canvasElCtx: function()
44325     {
44326         return this.el.select('canvas',true).first().dom.getContext('2d');
44327     },
44328     
44329     getImage: function(type)
44330     {
44331         if(this.is_empty) {
44332             return false;
44333         }
44334         
44335         // encryption ?
44336         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44337     },
44338     
44339     drawFromImage: function(img_src)
44340     {
44341         var img = new Image();
44342         
44343         img.onload = function(){
44344             this.canvasElCtx().drawImage(img, 0, 0);
44345         }.bind(this);
44346         
44347         img.src = img_src;
44348         
44349         this.is_empty = false;
44350     },
44351     
44352     selectImage: function()
44353     {
44354         this.fileEl().dom.click();
44355     },
44356     
44357     uploadImage: function(e)
44358     {
44359         var reader = new FileReader();
44360         
44361         reader.onload = function(e){
44362             var img = new Image();
44363             img.onload = function(){
44364                 this.reset();
44365                 this.canvasElCtx().drawImage(img, 0, 0);
44366             }.bind(this);
44367             img.src = e.target.result;
44368         }.bind(this);
44369         
44370         reader.readAsDataURL(e.target.files[0]);
44371     },
44372     
44373     // Bezier Point Constructor
44374     Point: (function () {
44375         function Point(x, y, time) {
44376             this.x = x;
44377             this.y = y;
44378             this.time = time || Date.now();
44379         }
44380         Point.prototype.distanceTo = function (start) {
44381             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44382         };
44383         Point.prototype.equals = function (other) {
44384             return this.x === other.x && this.y === other.y && this.time === other.time;
44385         };
44386         Point.prototype.velocityFrom = function (start) {
44387             return this.time !== start.time
44388             ? this.distanceTo(start) / (this.time - start.time)
44389             : 0;
44390         };
44391         return Point;
44392     }()),
44393     
44394     
44395     // Bezier Constructor
44396     Bezier: (function () {
44397         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44398             this.startPoint = startPoint;
44399             this.control2 = control2;
44400             this.control1 = control1;
44401             this.endPoint = endPoint;
44402             this.startWidth = startWidth;
44403             this.endWidth = endWidth;
44404         }
44405         Bezier.fromPoints = function (points, widths, scope) {
44406             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44407             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44408             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44409         };
44410         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44411             var dx1 = s1.x - s2.x;
44412             var dy1 = s1.y - s2.y;
44413             var dx2 = s2.x - s3.x;
44414             var dy2 = s2.y - s3.y;
44415             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44416             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44417             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44418             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44419             var dxm = m1.x - m2.x;
44420             var dym = m1.y - m2.y;
44421             var k = l2 / (l1 + l2);
44422             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44423             var tx = s2.x - cm.x;
44424             var ty = s2.y - cm.y;
44425             return {
44426                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44427                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44428             };
44429         };
44430         Bezier.prototype.length = function () {
44431             var steps = 10;
44432             var length = 0;
44433             var px;
44434             var py;
44435             for (var i = 0; i <= steps; i += 1) {
44436                 var t = i / steps;
44437                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44438                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44439                 if (i > 0) {
44440                     var xdiff = cx - px;
44441                     var ydiff = cy - py;
44442                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44443                 }
44444                 px = cx;
44445                 py = cy;
44446             }
44447             return length;
44448         };
44449         Bezier.prototype.point = function (t, start, c1, c2, end) {
44450             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44451             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44452             + (3.0 * c2 * (1.0 - t) * t * t)
44453             + (end * t * t * t);
44454         };
44455         return Bezier;
44456     }()),
44457     
44458     throttleStroke: function(fn, wait) {
44459       if (wait === void 0) { wait = 250; }
44460       var previous = 0;
44461       var timeout = null;
44462       var result;
44463       var storedContext;
44464       var storedArgs;
44465       var later = function () {
44466           previous = Date.now();
44467           timeout = null;
44468           result = fn.apply(storedContext, storedArgs);
44469           if (!timeout) {
44470               storedContext = null;
44471               storedArgs = [];
44472           }
44473       };
44474       return function wrapper() {
44475           var args = [];
44476           for (var _i = 0; _i < arguments.length; _i++) {
44477               args[_i] = arguments[_i];
44478           }
44479           var now = Date.now();
44480           var remaining = wait - (now - previous);
44481           storedContext = this;
44482           storedArgs = args;
44483           if (remaining <= 0 || remaining > wait) {
44484               if (timeout) {
44485                   clearTimeout(timeout);
44486                   timeout = null;
44487               }
44488               previous = now;
44489               result = fn.apply(storedContext, storedArgs);
44490               if (!timeout) {
44491                   storedContext = null;
44492                   storedArgs = [];
44493               }
44494           }
44495           else if (!timeout) {
44496               timeout = window.setTimeout(later, remaining);
44497           }
44498           return result;
44499       };
44500   }
44501   
44502 });
44503
44504  
44505
44506